📄 cgameframework.cpp
字号:
// Copyright 2002 Kenneth Guy,
//
// CGameFramework.cpp
/** \file CGameFramework.cpp
implementation of class CGameFramework */
//
#include <e32std.h>
#include "CGameFramework.h"
#include "CGame.h"
#include "TGamePanics.h"
/** Leave safe constuction.
Not really nessesary as there is no ConstuctL, but there might
be in the future */
CGameFramework* CGameFramework::NewL(){
CGameFramework* self= new (ELeave) CGameFramework;
return(self);
};
/** delete level information */
CGameFramework::~CGameFramework() {
delete iLevel;
delete iMap;
};
/** Cancel paused game.
Simulates a quit key press then starts the game running. The game
thread has to run so it can delete the CGame object.
*/
void CGameFramework::CancelL() {
iData.iKeys |= TGameData::EQuit;
RunL(EFalse);
}
/** Game thread start point.
This creates a CGame object and keeps calling Play on it while
the player has lives, hasn't finished the level and hasn't quit.
The TGameData comunicates the level information and key presses
to this thread and comunicates the score, lives left and outcome
of the level back to the ui thread.
\param aGameData a TGameData*
\return error code if we failed to create CGame or CTrapCleanup
objects
*/
TInt CGameFramework::GameThread(TAny* aGameData) {
// create a cleanup stack
CTrapCleanup* cleanup=CTrapCleanup::New();
if(cleanup==NULL)
return KErrNoMemory;
TInt error=KErrNone;
TGameData& data= (*STATIC_CAST(TGameData*,aGameData));
// look while we have lives, haven't got to the end of the level,
// 'Q' hasn't been pressed, and haven't got an error creating the game.
while(data.iLives!=0 && !(data.iKeys & TGameData::EQuit) &&
data.iLevelCompleted==EFalse && error==KErrNone) {
// create a game and call play on it.
CGame *game=NULL;
TRAPD(error,game=CGame::NewL(data));
if(error==KErrNone) {
game->Play();
delete game;
}
}
delete cleanup;
return error;
};
/** Allow the ui access to the players score*/
TInt CGameFramework::Score() {
return(iData.iScore);
}
/** Run a level of the game.
This class creates a connection to the window server, and
creates a window to cover the screen. Sets iData.iForceRedraw
so that is we are resuming a previous game the status screen
is updated in full. If this returns and the game hasn't been
paused the level has been deleted.
\param aReset whether to reset lives and score, ie whether this is a new game or resuming an old one.
\return Returns whether the level was paused, finished or the player died.
*/
CGameFramework::TGameState CGameFramework::RunL(TBool aReset) {
// assert that LoadLevelL has been called and succeded in
// loading a level
__ASSERT_ALWAYS(iLevel!=NULL && iMap!=NULL,TGamePanics::Panic(TGamePanics::ENoLevelLoaded));
// if this is a new game reset the score and lives
if(aReset!=EFalse) {
iData.iLives=3;
iData.iScore=0;
}
// connect to the window server.
RWsSession winSession;
User::LeaveIfError(winSession.Connect());
CleanupClosePushL(winSession);
// these can be whatever we like, as long as they are unique,
// as we have only one window and one windowgroup we don't
// actually use them.
const TUint32 KWindowHandle=1;
const TUint32 KWindowGroupHandle=2;
// create a window group so we can create a window
RWindowGroup winGroup(winSession);
User::LeaveIfError(winGroup.Construct(KWindowGroupHandle));
CleanupClosePushL(winGroup);
// create a blank window, blank windows can't be drawn on,
// however as the game code writes directly to the screen this
// isn't a problem.
RBlankWindow window(winSession);
User::LeaveIfError(window.Construct(winGroup,KWindowHandle));
CleanupClosePushL(window);
window.Activate();
window.SetSize(TSize(640,200));
// request focus events so we can pause the game thread when
// we move to the background, this stops us writing over some other
// apps screen.
window.EnableFocusChangeEvents();
// first time in we want want to redraw everything,
iData.iForceRedraw=ETrue;
// logon to the thread so we get informed when it exits.
iGameThread.Logon(iGameStatus);
iGameThread.Resume();
// play the game
TBool finished=Play(winSession);
// cancel any outstanding request for key strokes/focus changes etc
winSession.EventReadyCancel();
// and watching the state of the thread
iGameThread.LogonCancel(iGameStatus);
// I think this is the EventReady completing.
User::WaitForAnyRequest();
// remove our window
CleanupStack::PopAndDestroy(3,&winSession); //winGroup, window
if(finished==EFalse) {
// this is the thread notifying that it has suspended
User::WaitForAnyRequest();
// we have just paused the thread/game
return EPaused;
} else {
// game finished, so close the thread, and delete the level
iGameThread.Close();
delete iLevel;
iLevel=NULL;
delete iMap;
iMap=NULL;
User::Heap().Compress();
// did the player complete the level or die
if(iData.iLevelCompleted==EFalse) {
return EDied;
} else {
return ELevelCompleted;
}
}
};
/** Loop getting window server events.
This class loops waiting for requests.
If it is a window server key event we update the iData.iKeys bitmask
to tell the game thread that a key was pressed.
If it is a window server key event 'P' or a focus lost event we suspend
the game thread and return.
If it is the game thread exiting we return.
Otherwise we allow the active scheduler to handle it.
\return ETrue if game thread completed, EFalse if it was suspended.
*/
TBool CGameFramework::Play(RWsSession &aWindSession) {
TRequestStatus normalEvent;
// listen for key strokes/focus changes etc.
aWindSession.EventReady(&normalEvent);
for(;;) {
User::WaitForAnyRequest();
if(normalEvent!=KRequestPending) {
TWsEvent event;
aWindSession.GetEvent(event);
aWindSession.EventReady(&normalEvent);
switch(event.Type()) {
case EEventKeyDown: {
TKeyEvent *key=event.Key();
switch(key->iScanCode) {
case EStdKeyLeftArrow: iData.iKeys |= TGameData::ELeft; break;
case EStdKeyRightArrow: iData.iKeys |= TGameData::ERight; break;
case EStdKeyUpArrow: iData.iKeys |= TGameData::EUp; break;
case EStdKeyDownArrow: iData.iKeys |= TGameData::EDown; break;
case 'Q': iData.iKeys |= TGameData::EQuit; break;
case 'A': iData.iKeys |= TGameData::EAction1; break;
case EStdKeySpace: iData.iKeys |= TGameData::EAction2; break;
case EStdKeyLeftShift: iData.iKeys |= TGameData::EAction3; break;
case EStdKeyEnter: iData.iKeys |= TGameData::EAction4; break;
case 'P': {
iGameThread.Suspend();
return EFalse;
}
}
}
break;
case EEventKeyUp: {
TKeyEvent *key=event.Key();
switch(key->iScanCode) {
case EStdKeyLeftArrow: iData.iKeys &= ~TGameData::ELeft; break;
case EStdKeyRightArrow: iData.iKeys &= ~TGameData::ERight; break;
case EStdKeyUpArrow: iData.iKeys &= ~TGameData::EUp; break;
case EStdKeyDownArrow: iData.iKeys &= ~TGameData::EDown; break;
case 'A': iData.iKeys &= ~TGameData::EAction1; break;
case EStdKeySpace: iData.iKeys &= ~TGameData::EAction2; break;
case EStdKeyLeftShift: iData.iKeys &= ~TGameData::EAction3; break;
case EStdKeyEnter: iData.iKeys &= ~TGameData::EAction4; break;
}
}
break;
case EEventFocusLost:
// pause game if we go into the background
iGameThread.Suspend();
return EFalse;
break;
}
} else if(iGameStatus!=KRequestPending) {
// thread ended
return ETrue;
} else {
// event meant for active scheduler if there is one
// I don't think it should never get here
CActiveScheduler *sched=CActiveScheduler::Current();
if(sched!=NULL) {
TInt error;
sched->RunIfReady(error,0);
}
}
}
}
/** Load level and map files.
This loads the level and map files setting the iData.iLevel members
to the information in the files.
It also creates the game thread but doesn't resume it.
\param aLevelName full path and file name of level file.
\param aMapName full path and file name of map file.
\param aResetShip whether to reset ship powerups to level data.
*/
void CGameFramework::LoadLevelL(const TDesC& aLevelName,const TDesC& aMapName, TBool aResetShip) {
__ASSERT_ALWAYS(iLevel==NULL && iMap==NULL,TGamePanics::Panic(TGamePanics::ELoadLevelWithLevelLoaded));
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
// load level file
RFile levelFile;
User::LeaveIfError(levelFile.Open(fs,aLevelName,EFileRead));
CleanupClosePushL(levelFile);
TInt size;
User::LeaveIfError(levelFile.Size(size));
size-=16; //skip uids on front of file
HBufC8 *levelDes= HBufC8::NewLC(size);
TPtr8 levelPtr=levelDes->Des();
User::LeaveIfError(levelFile.Read(16,levelPtr,size));
// load map file
RFile mapFile;
User::LeaveIfError(mapFile.Open(fs,aMapName,EFileRead));
CleanupClosePushL(mapFile);
User::LeaveIfError(mapFile.Size(size));
size-=16; //skip uids on front of file
HBufC8 *mapDes= HBufC8::NewLC(size);
TPtr8 mapPtr=mapDes->Des();
User::LeaveIfError(mapFile.Read(16,mapPtr,size));
// create a thread within which to run the game,
_LIT(KGameThread,"RealGame");
// 4k stack,
const TInt KStackSize=4*1024;
// create but don't resume as the iData isn't set up yet.
User::LeaveIfError(iGameThread.Create(KGameThread,GameThread,KStackSize,NULL,&iData));
// as we want to respond to key presses the game thread
// has a lower priority than getting events
iGameThread.SetPriority(EPriorityLess);
CleanupStack::Pop(mapDes);
iMap=mapDes;
CleanupStack::PopAndDestroy(&mapFile);
CleanupStack::Pop(levelDes);
iLevel=levelDes;
CleanupStack::PopAndDestroy(2,&fs); // levelFile
// setup level data
TInt32* level= (TInt32*) (iLevel->Ptr());
TInt32* map= (TInt32*) (iMap->Ptr());
iData.iKeys=0;
iData.iLevelCompleted=EFalse;
iData.iLevel.iMapData=(TInt16*) (iMap->Ptr()+map[0]);
iData.iLevel.iPaths=(TInt16*) (iMap->Ptr()+map[1]);
iData.iLevel.iBackgrounds=(TUint16*) (iLevel->Ptr()+level[0]);
iData.iLevel.iSprites=(TUint16*) (iLevel->Ptr()+level[1]);
iData.iLevel.iStatus=(TUint16*) (iLevel->Ptr()+level[2]);
iData.iLevel.iShipSprite=(TInt16) map[2];
iData.iLevel.iShipDeathStartSprite=(TInt16) map[3];
iData.iLevel.iShipDeathEndSprite=(TInt16) map[4];
iData.iLevel.iBadGuyDeathStartSprite=(TInt16) map[5];
iData.iLevel.iBadGuyDeathEndSprite=(TInt16) map[6];
iData.iLevel.iBulletPower1_StartSprite=(TInt16) map[7];
iData.iLevel.iBulletPower2_StartSprite=(TInt16) map[8];
iData.iLevel.iBulletPower3_StartSprite=(TInt16) map[9];
iData.iLevel.iBulletPower4_StartSprite=(TInt16) map[10];
iData.iLevel.iShipStartXPos=(TInt16) map[11];
iData.iLevel.iShipStartYPos=(TInt16) map[12];
iData.iLevel.iShipStartHealth=(TInt16) map[13];
if(aResetShip!=EFalse) {
iData.iLevel.iShipPowerups=(TInt16) map[14];
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -