A first "shoot em up" game

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
ehenkes
Posts: 47
Joined: Sun Aug 03, 2008 2:52 pm
Location: Germany
Contact:

A first "shoot em up" game

Post by ehenkes »

Perhaps my first try helps other beginners to start. Its the begin of a classical shooter game. How would you implement the level control?

Why do I need in row 189 this "break"? EDIT: problem solved, device->run() was lacking in the while loop.

Code: Select all

// IrrSpiel.cpp from www.HenkesSoft.de
// First 3D-Game "Muhaha Code1"
//
// Irrlicht types cf. irrTypes.h, e.g. 
// s32: __int32, u32: unsigned __int32, 
// f32: float,   f64: double

#include "stdafx.h"    // MS VisualC++ 
#include <ctime>       // clock, time
#include <cstdlib>      // srand, rand
#include <iostream>    // cout, cin, endl
#include <limits>      // wait()
#include <irrlicht.h>  // grafic engine (free)
#include <irrklang.h>	 // sound system (free for private use)

// irrlicht namespace
using namespace irr; using namespace core; using namespace scene;
using namespace video; using namespace io; using namespace gui; using namespace std;

// linker commands
#pragma comment(lib, "irrlicht.lib")
#pragma comment(lib, "irrklang.lib")

void wait() // prohibits "unexpected shutdown" of console
{
 std::cin.clear();
 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
 std::cin.get();
}

void drawScene(IVideoDriver* driver, ISceneManager* smgr,	IGUIEnvironment* guienv)
{
  driver->beginScene(true, true, SColor(0,200,200,200));
	smgr->drawAll();
	guienv->drawAll();
	driver->endScene();
}

// Event Handler
class MyEventReceiver : public IEventReceiver
{
	public:
    virtual bool OnEvent(const SEvent& event)
		{
        if (event.EventType == EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
        return false;
		}
    virtual bool IsKeyDown(EKEY_CODE keyCode) const {return KeyIsDown[keyCode];}
    MyEventReceiver(){for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i) KeyIsDown[i] = false;}
  private:
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};

struct MyBullet
{
		IAnimatedMeshSceneNode* nodeBullet;
		bool hasHit;	
		bool isValid;
};

// Simple collision functions
bool isCollisionA(ISceneNode* one, ISceneNode* two, int size)
{
   return( one->getAbsolutePosition().getDistanceFrom(two->getAbsolutePosition()) < size ); 
} 

bool isCollisionB(ISceneNode* one, ISceneNode* two)
{
	 return ( one->getTransformedBoundingBox().intersectsWithBox( two->getTransformedBoundingBox() ) ); 
} 

// entry point of program
s32 main()
{
	// init randomizer
	srand(unsigned(time(0)));

	// start Irrlicht 
	MyEventReceiver receiver;
	IrrlichtDevice* device = createDevice( 
	EDT_DIRECT3D9, dimension2d<s32>(800,600),32,0,0,0,&receiver);
	IVideoDriver*    driver = device->getVideoDriver();
	ISceneManager*   smgr   = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();
	device->getCursorControl()->setVisible(false); // no mouse cursor
	
	// "Skybox"  six times "black sky with some stars" (512*512 pixel)
	ISceneNode* SkyBox = smgr->addSkyBoxSceneNode	( 
	  driver->getTexture("../skybox/top.jpg"    ),
		driver->getTexture("../skybox/bottom.jpg" ),
		driver->getTexture("../skybox/front.jpg"  ),   
		driver->getTexture("../skybox/back.jpg"   ),   
		driver->getTexture("../skybox/right.jpg"  ),
		driver->getTexture("../skybox/left.jpg"   ));
    
	// Intro - Background Music - Camera
	IGUIImage* img = 
	guienv->addImage( driver->getTexture("../media/uglyface_starterImage.jpg"), position2d<int>(30,30));

  irrklang::ISound* snd = (irrklang::createIrrKlangDevice())->play2D(
		                       "../media/IrrlichtTheme.ogg", true, false, true);
	//IrrlichtTheme.ogg comes with Irrlicht Engine
	if(snd)	{	snd->setVolume(0.2f);	snd->drop(); }

	ICameraSceneNode* cam = smgr->addCameraSceneNode(0, vector3df(0,1000,0), vector3df(0,0,0));


	// SpaceShip //http://www.turbosquid.com/FullPreview/Index.cfm/ID/261430 (free)
	const vector3df STARTPOSSPACESHIP = vector3df(-200,0,0);
	const vector3df RESETPOSSPACESHIP = vector3df(-600,0,0);
	const s32 MAXSHOOTS_PER_SEC       =   10; 
  f32 velocitySpaceship							= 2250.0; 
	// Texture
	SMaterial matSpaceShip;
	matSpaceShip.setTexture(0, driver->getTexture("../media/orange.jpg"));
	matSpaceShip.Lighting = false;
  // Mesh
	IAnimatedMeshSceneNode* nodeSpaceShip;
	IAnimatedMesh* meshSpaceShip = smgr->getMesh("../media/spaceship.3ds");
	if (meshSpaceShip){
	   nodeSpaceShip = smgr->addAnimatedMeshSceneNode(meshSpaceShip);
	   nodeSpaceShip->setPosition(STARTPOSSPACESHIP);
		 nodeSpaceShip->getMaterial(0) = matSpaceShip;
	   nodeSpaceShip->setMaterialFlag(EMF_LIGHTING, false);
	}

	// Enemies
	const vector3df STARTPOSENEMY = vector3df( 700,0, 800);
	const vector3df ENDPOSENEMY   = vector3df(-600,0,-800);
	// Texture
	SMaterial matEnemy;
	matEnemy.setTexture(0, driver->getTexture("../media/orange.jpg"));// 512*512 pixel (orange color)
	matEnemy.Lighting = false;
  // Mesh
	IAnimatedMeshSceneNode* nodeEnemy;
	IAnimatedMesh* meshEnemy = smgr->getMesh("../media/vm21.3ds"); // really nice ;-)
		//http://www.turbosquid.com/FullPreview/Index.cfm/ID/222232 (free)
	if (meshEnemy){	 
		 nodeEnemy = smgr->addAnimatedMeshSceneNode(meshEnemy);
		 nodeEnemy->setPosition(STARTPOSENEMY);
		 nodeEnemy->getMaterial(0) = matEnemy;
		 nodeEnemy->setMaterialFlag(EMF_LIGHTING, false);
		 nodeEnemy->setScale(vector3df(20,20,20));   // x-,y-,z-scaling factors
		 nodeEnemy->setRotation(vector3df(0,90,30)); // rotate by ... degrees (x,y,z)
	}
	
	ISceneNodeAnimator* animFS = smgr->createFlyStraightAnimator(STARTPOSENEMY, ENDPOSENEMY, 2000, true); 
	if(animFS){
		nodeEnemy->addAnimator(animFS);  // move enemy
		animFS->drop();	
	}
	
  // Bullet
	const s32 MAXSHOOTS = 2000;   	
	MyBullet bullets[MAXSHOOTS];
	for(int i=0; i<MAXSHOOTS; ++i)
	{
		bullets[i].hasHit = false;
		bullets[i].nodeBullet = 0;
		bullets[i].isValid=false;
	}

	// Game data 
	u32 t        =    0;   // time
	s32 life     =   10;   // life of SpaceShip
	s32 echelon  =    1;   // attack waves
	s32 shoots   =    0;   // shoots of SpaceShip
	s32 hits     =    0;   // enemies hit
	s32 fps      = 1500;   // first value before exact measurement
	s32 level    =    0;   // start image
	
	
	/********************* "device->run()" with FPS and Info ********************/
	while(device->run())
	{
		if( (life <= 0) || (shoots > MAXSHOOTS) ) break;
		if(device->isWindowActive())
		{			
			f32 delta = velocitySpaceship/fps; // target 1.50
			while(!level)
			{
				if( receiver.IsKeyDown(KEY_RETURN) ) 
				{
					level = 1; // first game level
					img->setVisible(false);
				}
				drawScene(driver, smgr, guienv); 
				break; // It works, but why is it necessary??? 
			}

			// keys W A S D pressed?
			vector3df v = nodeSpaceShip->getAbsolutePosition();	
			
			if (receiver.IsKeyDown(KEY_UP))   {v.X += delta; }				
			if (receiver.IsKeyDown(KEY_DOWN)) {v.X -= delta; }
			if (receiver.IsKeyDown(KEY_LEFT)) {v.Z += delta; }
			if (receiver.IsKeyDown(KEY_RIGHT)){v.Z -= delta; }
			
			if (receiver.IsKeyDown(KEY_KEY_T)){velocitySpaceship = 5000.0f; }
			if (receiver.IsKeyDown(KEY_KEY_M)){velocitySpaceship = 2250.0f; }
			if (receiver.IsKeyDown(KEY_KEY_B)){velocitySpaceship = 1500.0f; }
			
      // check collision between bullets and enemies
			for(int i=0; i<MAXSHOOTS; ++i)
			{				
				if(bullets[i].nodeBullet && bullets[i].isValid && nodeEnemy)
				{
					if((bullets[i].nodeBullet->getAbsolutePosition().X) > 700) // upper border of window 
					{
						ISceneNodeAnimator* animDel = smgr->createDeleteAnimator(1); 
						if(animDel)
						{
							bullets[i].nodeBullet->addAnimator(animDel); // delete bullet
							bullets[i].isValid = false;						
							animDel->drop();	
						}
					}

					if(isCollisionB(bullets[i].nodeBullet,nodeEnemy) && (!bullets[i].hasHit)) 
					{
						bullets[i].hasHit = true;
						bullets[i].nodeBullet->setVisible(false);
						ISceneNodeAnimator* animDel = smgr->createDeleteAnimator(1); 
						if(animDel)
						{
							bullets[i].nodeBullet->addAnimator(animDel); // delete bullet
							bullets[i].isValid = false;						
							animDel->drop();	
						}
						++hits; 
						// Irrklang not free for comercial purpose!
						snd = (irrklang::createIrrKlangDevice())->play3D("../media/muhaha.wav", v, false, false, true); 
						// http://download.dual-players.eu/cs/cstrike/sound/dualplayers/ (free?)
						if(snd){ snd->setMinDistance(1000.0f); snd->setVolume(1.0f); snd->drop(); }


						// fireball
						IParticleSystemSceneNode* fireball;
						fireball = smgr->addParticleSystemSceneNode(false);
						fireball->setPosition( nodeEnemy->getAbsolutePosition() );
						fireball->setScale(vector3df(20.0f,20.0f,20.0f));
						fireball->setParticleSize(dimension2d<f32>(20.0f, 10.0f));
						IParticleEmitter* em = fireball->createBoxEmitter(
							aabbox3d<f32>(-70,0,-70,70,10,70),
							vector3df(0.0f,0.6f,0.0f), 80, 100, 
							SColor(0,255,255,255),SColor(0,255,255,255), 800,2000);
						fireball->setEmitter(em);
						em->drop();

						IParticleAffector* paf = fireball->createFadeOutParticleAffector();
						fireball->addAffector(paf);
						paf->drop();

						fireball->setMaterialFlag(video::EMF_LIGHTING, false);
						fireball->setMaterialTexture(0, driver->getTexture("../media/fireball.bmp"));
						fireball->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
					}
				}
			}			

			// check collision between spaceship and enemies
			if(isCollisionB(nodeSpaceShip,nodeEnemy))
			{
				v = RESETPOSSPACESHIP;
				--life;
			}

			// SPACE pressed? SHOOT!
			if (receiver.IsKeyDown(KEY_SPACE))
			{
				if (shoots > MAXSHOOTS)			
					break;

				static clock_t last_t;
				if( clock() > (last_t + CLOCKS_PER_SEC / MAXSHOOTS_PER_SEC) )
				{
					// Shoot sound
					irrklang::ISound* snd = (irrklang::createIrrKlangDevice())->play3D(
																	 "../media/impact.wav", v, false, false, true); 
					// impact.wav comes with Irrlicht Engine
					if (snd)
					{
						snd->setMinDistance(1000.0f);
						snd->setVolume(1.0f);	
						snd->drop();
					}
					last_t = clock();				
			    
					// bullet
					IAnimatedMeshSceneNode* bullet = 0;
					IAnimatedMesh* meshBullet = smgr->getMesh("../media/sphere.3ds");
					if (meshBullet)
					{
						 bullet = smgr->addAnimatedMeshSceneNode(meshBullet);
						 bullet->setPosition(v);
						 bullet->getMaterial(0) = matSpaceShip;
						 bullet->setMaterialFlag(EMF_LIGHTING, false);
					}
					bullet->setScale(vector3df( 1.0f, 0.3f, 0.3f ));
					ISceneNodeAnimator* animFS = smgr->createFlyStraightAnimator(
						                         v,vector3df(1000,v.Y,v.Z), 500, false); 
					if(animFS)
					{
						bullet->addAnimator(animFS);  // move bullet
						animFS->drop();	
					}

					bullets[shoots].nodeBullet = bullet;
					bullets[shoots].isValid = true;
					++shoots;
				}//if
			}//if
						
			// borders for moving SpaceShip   
			if(v.X>  100)v.X=  100;
			if(v.X< -600)v.X= -600;
			if(v.Z>  900)v.Z=  900;
			if(v.Z< -900)v.Z= -900;
			
			// define position 
			nodeSpaceShip->setPosition(v);

			// draw scene 
      drawScene(driver, smgr, guienv);

			// deliver information in window caption
			fps = driver->getFPS();
			stringw str = L"HenkesSoft \"MUHAHA Code1\"  FPS: "; 
			str += fps;
			str += L"  Wave: ";
			str += echelon;
			str += L"  Shoots: ";
			str += shoots;
			str += L"  Hits: ";
			str += hits;
			str += L"  Life: ";
			str += life;
			device->setWindowCaption(str.c_str());
			if( (life <= 0) || (shoots > MAXSHOOTS) )			
				break;
		}//if
	}//while
  
	device->drop(); // delete object 
  cout << "GAME OVER - Press ENTER (twice), please. Thank you for playing this game." << endl;
	wait(); // windows console specialty ;-)
}//main
Last edited by ehenkes on Thu Aug 07, 2008 9:02 pm, edited 1 time in total.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

You need the break because you're in a while loop, but really you shouldn't use a while loop at all because it always breaks out at the end of the first iteration so it's just acting like an if statement.

If you want people to try out your code then you need to provide a download with an exe and all the necessary assets, dlls etc. I'd love to look at your code but i really don't want to have to go to the bother of compiling it myself and then find out that i don't have things like uglyface_starterImage.jpg and all your audio and models which could result in crashes or just a completely non-functioning game...

And in fact yes your code will crash if you don't provide the assets because you're using nodeEnemy without checking it was actually created (which it won't be because meshEnemy isn't created due to not being able to load vm21.3ds.

And what do you mean about how to implement level control? How to switch between levels? That stuffs been covered quite a few times in the forum so you should be able to do a search and find something relevant.
Image Image Image
B@z
Posts: 876
Joined: Thu Jan 31, 2008 5:05 pm
Location: Hungary

Post by B@z »

JP wrote:You need the break because you're in a while loop, but really you shouldn't use a while loop at all because it always breaks out at the end of the first iteration so it's just acting like an if statement.

And what do you mean about how to implement level control? How to switch between levels? That stuffs been covered quite a few times in the forum so you should be able to do a search and find something relevant.
i think it wasnt a question for you, but for beginners, to solve this problem by themself.
you know, learning while making something... xD or something like that.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

That's not my impression of it, and a bit of a weird thing to do if it is :lol:
Image Image Image
ehenkes
Posts: 47
Joined: Sun Aug 03, 2008 2:52 pm
Location: Germany
Contact:

Post by ehenkes »

Please excuse, here are the binaries and resources.
http://www.henkessoft.de/Sonstiges/IrrSpiel.zip
Yes, you are right. All pointers should be checked, whether they are really existent.

Code: Select all

while(!level)
         {
            if( receiver.IsKeyDown(KEY_RETURN) )
            {
               level = 1; // first game level
               img->setVisible(false);
            }
            drawScene(driver, smgr, guienv);
            break; // It works, but why is it necessary???
         } 
After pressing RETURN level is set to 1. Hence, (!level) should be false. I do not understand, why I need this break to leave the while loop. Maybe, I'm a little bit blind. :?
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

ehenkes wrote: After pressing RETURN level is set to 1. Hence, (!level) should be false. I do not understand, why I need this break to leave the while loop. Maybe, I'm a little bit blind. :?
the reason is because the engine is not updated in this loop, so it doesn't handle any events... ;)
try to use device->run(); instead of the break; !!! ;)

or even better this way:

Code: Select all

while(device->run() && !level){
  if( receiver.IsKeyDown(KEY_RETURN) ){
    level = 1; // first game level
    img->setVisible(false);
  }
  drawScene(driver, smgr, guienv);
}
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
ehenkes
Posts: 47
Joined: Sun Aug 03, 2008 2:52 pm
Location: Germany
Contact:

Post by ehenkes »

@Acki: thank you very much. :lol:
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

The download doesn't work btw, says there's a problem and reinstalling it might help... did you use MSVC to compile it? Shouldn't be a problem for me though i wouldn't have thought as i have MSVC on this PC...
Image Image Image
ehenkes
Posts: 47
Joined: Sun Aug 03, 2008 2:52 pm
Location: Germany
Contact:

Post by ehenkes »

The download doesn't work btw, ...
Currently, it works.
did you use MSVC to compile it?
yes, MSVC++ 2008 and Irrlicht 1.4.1 (great combination).

Improved the sound by using

Code: Select all

// start Irrklang sound engine and load sound waves
irrklang::ISoundEngine* sndEngine = irrklang::createIrrKlangDevice();
irrklang::ISoundSource* backgroundSound = sndEngine->addSoundSourceFromFile("../media/IrrlichtTheme.ogg");
irrklang::ISoundSource* shootSound = sndEngine->addSoundSourceFromFile("../media/impact.wav");
irrklang::ISoundSource* muhahaSound = sndEngine->addSoundSourceFromFile("../media/muhaha.wav");

//...

irrklang::ISound* snd = sndEngine->play2D(backgroundSound, true, false, true);
if(snd)    { snd->setVolume(0.2f);    snd->drop(); }

// etc.

// ...

device->drop();    // delete grafic engine
sndEngine->drop(); // delete sound engine
Raman
Posts: 3
Joined: Fri Aug 08, 2008 3:27 pm

Post by Raman »

THANK YOU SO MUCH! I was looking for a code to limit the amount of bullets per sec and I found it! Thank You so much!
Post Reply