cgame.cpp
来自「symbian 的一个 二维飞行游戏 源码 及相关技术文章」· C++ 代码 · 共 477 行
CPP
477 行
// Copyright 2002 Kenneth Guy,
//
// CGame.cpp
/** \file CGame.cpp
implementation of class CGame */
#include <e32std.h>
#include <e32base.h>
#include "CMap.h"
#include "CGame.h"
#include "TBadGuy.h"
#include "TPlayersShip.h"
#include "TBullet.h"
#include "TGameData.h"
/** Maximum number of bad guys on screen at one time.
When all array slots are used adding a new bad guy
doesn't do anything.
*/
const TInt KMaxBadGuys=125;
/** Maximum number of players bullets on screen at one time.
When all array slots are used shooting (CGame::Shoot())
doesn't do anything.
*/
const TInt KMaxBullets=8;
/** Standard leave safe construction */
CGame* CGame::NewL(TGameData &aData) {
CGame *self= new (ELeave) CGame(aData);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return(self);
}
/** constructor */
CGame::CGame(TGameData& aData) : iData(aData), iLastFired(0) {
};
/** closes the status screen and delete member objects */
CGame::~CGame() {
iStatus.Close();
delete iMap;
delete [] iBadGuys;
delete [] iBullets;
delete iPlayersShip;
};
/** Second phase construtor.
Creates status screen, creates players ship, creates array of slots for
bad guys, creates array of bullets and sets them up as strength 1
*/
void CGame::ConstructL() {
// create the status screen area
User::LeaveIfError(iStatus.Open(iData.iLevel.iStatus));
// create the players ship as defined by the level data
iPlayersShip = new (ELeave) TPlayersShip(iData.iLevel.iShipStartXPos,
iData.iLevel.iShipStartYPos,
iData.iLevel.iShipStartHealth,
iData.iLevel.iShipSprite,
iData.iLevel.iShipDeathStartSprite,
iData.iLevel.iShipDeathEndSprite);
// create slots for KMaxBadGuy bad guys
// (TBadGuy is used for bonuses and sheilds as well as bad guys)
iBadGuys= new (ELeave) TBadGuy[KMaxBadGuys];
// set up players bullets at power one
iBullets= new (ELeave) TBullet[KMaxBullets];
for(TInt i=0;i<KMaxBullets;i++) {
iBullets[i].Set(iData.iLevel.iBulletPower1_StartSprite+i,1);
}
TInt16 count=iData.iLevel.iShipPowerups;
while(count--) {
Bonus(EBonusPowerup);
}
// create the offscreen buffer, and plot the first screen full of
// the map
iMap=CMap::NewL(*this,iData.iLevel.iBackgrounds,iData.iLevel.iMapData);
// CMap may calls back into CGame to add bad guys, if there
// are any bad guys in the first 11 lines of the map this will
// happen during construction, so CGame should be valid when
// constructing iMap,
};
/** Main game loop.
This loop runs while the player hasn't died, finished the level,
or quit.
The player dies when players ship health has reached zero, and the
explosion animation has finished.
The player finishes the level when the map reaches the end and the
player flies off the right hand side of the screen.
The game quits if the TGameData::EQuit bit is set in TGameData::iKeys
The loop consists of:
<ul>
<li> Handle key presses - move ship/shoot </li>
<li> Move bad guys and bullets </li>
<li> Draw sprites onto offscreen map (player, badguys, bullets) </li>
<li> Delay so that each loop takes at least 2 ticks </li>
<li> Copy offscreen map to screen </li>
<li> Erase sprites from offscreen map </li>
<li> Handle collisions between badguys and bullets or players ship </li>
<li> Handle collisions between players ship and landscape </li>
<li> return is player is dead </li>
<li> return is player has finished </li>
<li> Update status pane </li>
<li> Scroll the map, (may add new bad guys) </li>
</ul>
*/
void CGame::Play() {
TUint lasttick=0;
while(!(iData.iKeys & TGameData::EQuit)) {
// handle key presses
HandleKeys();
// move bullets and bad guys
MoveThings();
// plot the ship/bullets & badguys into offscreen buffer
DrawEveryThing();
// make sure each loop takes at least 2 ticks
TUint tick = User::TickCount();
while((tick-lasttick)<2)
tick = User::TickCount();
lasttick=tick;
// copy the offscreen buffer to the screen
iMap->BlitAsm();
// remove all the sprites we ploted in reverse order
RemoveEveryThing();
// handle bad guy collisions
Collisions();
// crash if player hit the landscape
if(iPlayersShip->Hit()==TSprite32x24::EHitLandScape)
iPlayersShip->SetHealth(0);
// Handle crashing ship
if(iPlayersShip->Dead()!=EFalse) {
iData.iLives--;
return;
}
// handle end of level (when map has finished, and player flies
// off the right hand side of the screen)
if(iMap->Finished()!=EFalse && iPlayersShip->iX==304 &&
iPlayersShip->Dieing()==EFalse) {
iData.iLevelCompleted=ETrue;
// update level data so that next level can start with same
// health / powerups
iData.iLevel.iShipPowerups=iPowerups;
iData.iLevel.iShipStartHealth=iPlayersShip->Health();
return;
}
// plot status window
if(iData.iForceRedraw==EFalse) {
iStatus.Update(iData.iScore,iData.iLives,iPlayersShip->Health());
} else {
iStatus.Draw(iData.iScore,iData.iLives,iPlayersShip->Health());
iData.iForceRedraw=EFalse;
}
// scroll the offset screen map, plot another line of map sprites
// if nessesary.
iMap->ScrollMap();
}
};
/** Collect bonuses.
This is called to add an extra life, or powerup a bullet when
the players ship collides with a bonus sprite.
\param aBonus Type of bonus
*/
void CGame::Bonus(TGameBonus aBonus) {
switch(aBonus) {
case EBonusLife:
iData.iLives++;
break;
case EBonusPowerup: {
// remember the number of these so that if the player completes
// the level they can be carried to the next level
iPowerups++;
// find the bullet with the least power and increase
// its power by one, changing its sprite at the same time.
// if all bullets are at full strength nothing happens.
TInt16 minPower=iBullets[0].Strength();
TInt powerUp=0;
for(TInt i=1;i<KMaxBullets;i++) {
const TInt16 strength=iBullets[i].Strength();
if(strength < minPower) {
powerUp=i;
minPower = strength;
}
}
switch(minPower) {
case 0:
iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower1_StartSprite,1);
break;
case 1:
iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower2_StartSprite,2);
break;
case 2:
iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower3_StartSprite,3);
break;
case 3:
iBullets[powerUp].Set(powerUp+iData.iLevel.iBulletPower4_StartSprite,4);
break;
}
}
break;
}
}
/** First bad guy explosion sprite.
\return Sprite number of first sprite to use for bad guy explossions
*/
TInt16 CGame::FirstExplosionSprite() {
return iData.iLevel.iBadGuyDeathStartSprite;
}
/** Last bad guy explosion sprite.
\return Sprite number of last sprite to use for bad guy explossions (inclusive)
*/
TInt16 CGame::LastExplosionSprite() {
return iData.iLevel.iBadGuyDeathEndSprite;
}
/** Give the player some points */
void CGame::AddScore(TInt aScore) {
iData.iScore+=aScore;
}
/** Damage bad guys with correct id.
\param aId Id of bad guys to damage
\param aHealth amount of damage to do
*/
void CGame::DamageBadGuys(TInt16 aId,TInt16 aHealth) {
for(TInt i=0;i<KMaxBadGuys;i++) {
if(iBadGuys[i].Used()!= EFalse && aId == iBadGuys[i].Id()) {
iBadGuys[i].Damage(aHealth,*this);
}
}
}
/** Work out what hit what.
Check the TSprite32x24::Hit for each of the bad guys to see what is
was plotted over. Call the HitShip() or HitBullet() members as nessesary.
*/
void CGame::Collisions() {
for(TInt i=KMaxBadGuys-1;i>=0;--i) {
if(iBadGuys[i].Used()!=EFalse) {
TSprite32x24::THit what=iBadGuys[i].Hit();
if(what==TSprite32x24::EHitShip) {
// badguy has hit player
iBadGuys[i].HitShip(*iPlayersShip,*this);
} else if(what >=TSprite32x24::EHitBullet1 &&
what <=TSprite32x24::EHitBullet8) {
// badguy has hit a bullet
TInt bulletNo = what - TSprite32x24::EHitBullet1;
iBadGuys[i].HitBullet(iBullets[bulletNo],*this);
}
}
}
}
/** Erase all sprites.
Put back the saved backgrounds to erase the sprites.
This is done in the reverse of the order they were
plotted so that the background is restored correctly
*/
void CGame::RemoveEveryThing() {
TInt i;
for(i=KMaxBadGuys-1;i>=0;--i) {
if(iBadGuys[i].Used()!=EFalse)
iBadGuys[i].Remove();
}
for(i=KMaxBullets-1;i>=0;--i) {
if(iBullets[i].Used()!=EFalse)
iBullets[i].Remove();
}
iPlayersShip->Remove();
}
/** Draw all sprites.
Draw the badguys, bullets and players ship.
The order is players ship and bullets first so that when the
bad guys are plotted the collision information for the bad
guy will include either bullets or the players ship if the badguy's
position intersects with a bullet or the player.
*/
void CGame::DrawEveryThing() {
const TUint16 *sprites=iData.iLevel.iSprites;
iPlayersShip->Plot(*iMap,sprites);
TInt i;
for(i=0;i<KMaxBullets;++i) {
if(iBullets[i].Used()!=EFalse)
iBullets[i].Plot(*iMap,sprites);
}
for(i=0;i<KMaxBadGuys;++i) {
if(iBadGuys[i].Used()!=EFalse)
iBadGuys[i].Plot(*iMap,sprites);
}
}
/** Move bullets and bad guys.
*/
void CGame::MoveThings() {
for(TInt k=0;k<KMaxBullets;k++) {
if(iBullets[k].Used()!=EFalse) {
iBullets[k].Move();
}
}
for(TInt i=0;i<KMaxBadGuys;++i) {
if(iBadGuys[i].Used()!=EFalse) {
iBadGuys[i].Move(iPlayersShip->iX,iPlayersShip->iY,iData.iLevel.iPaths,*this);
}
}
};
/** Add a bad guy to the bad guy array.
If an empty slot is available create a new bad guy with
these settings.
\param aBadGuy bad guy settings to copy.
*/
void CGame::AddBadGuy(const TBadGuy &aBadGuy) {
TInt empty=0;
for(;empty<KMaxBadGuys && iBadGuys[empty].Used()!=EFalse;empty++) {};
if(empty>=KMaxBadGuys) return;
iBadGuys[empty]=aBadGuy;
}
/** Add a bad guy to the bad guy array.
If an empty slot is available create a new bad guy with
these settings.
\param aType first map data TInt16
\param aArgs 7 more map data TInt16s see TBadGuy::Init()
*/
void CGame::AddBadGuy(TInt16 aType,const TInt16 aArgs[]) {
TInt empty=0;
for(;empty<KMaxBadGuys && iBadGuys[empty].Used()!=EFalse;empty++) {};
if(empty>=KMaxBadGuys) return;
iBadGuys[empty].Init(aType,aArgs);
};
/** Handle movement and action keys.
Moves the players ship and fires the ships guns.
It will allow the player to move while dieing but not to shoot
while dieing.
*/
void CGame::HandleKeys() {
//movement
TInt x=0;
TInt y=0;
if(iData.iKeys & TGameData::EUp) y-=6;
if(iData.iKeys & TGameData::EDown) y+=6;
if(iData.iKeys & TGameData::ELeft) x-=6;
if(iData.iKeys & TGameData::ERight) x+=6;
iPlayersShip->MoveBy(x,y,iMap->Finished());
// shooting
// can't shoot while dieing,
if(iData.iKeys & TGameData::EAction3 && iPlayersShip->Dieing()==EFalse) {
// de-bounce fire key
iData.iKeys = iData.iKeys & ~TGameData::EAction3;
Shoot();
}
}
/** Shoot.
Make one of the bullet slots live. The function tries to rotate
the bullet slots so that if one is a greater power than the others
it is used once every KMaxBullets shots.
*/
void CGame::Shoot() {
// add another bullet if there is a free slot
// try and rotate bullet slots,
TInt i;
for(i=iLastFired+1;i<KMaxBullets;i++) {
if(iBullets[i].Used()==EFalse) {
iLastFired=i;
iBullets[i].Shoot(iPlayersShip->iX+12,iPlayersShip->iY);
return;
}
}
for(i=0;i<=iLastFired && i<KMaxBullets;i++) {
if(iBullets[i].Used()==EFalse) {
iLastFired=i;
iBullets[i].Shoot(iPlayersShip->iX+12,iPlayersShip->iY);
return;
}
}
}
/** Speed map is scrolling at.
The map has only two speeds, 2 words a second (4 pixels) or
stopped;
*/
TInt16 CGame::MapSpeed() {
if(iMap->Finished()==EFalse) {
return -2;
} else {
return 0;
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?