⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cengine.cpp

📁 Symbian开发提高的一个很好的实例,其实现了一个非常好理解的Symbian游戏开发流程
💻 CPP
字号:
#include <eikenv.h>
#include <fbs.h>
#include <aknutils.h>
#include <s32file.h>
#include <aknquerydialog.h> 
#include <bautils.h>

#include "CEngine.h"
#include "CGameTimer.h"
#include "CSplash.h"
#include "CMenu.h"
#include "CGameTetris.h"
#include "TetrisAppUi.h"
#include <Tetris.rsg>
#include "Tetris.hrh"
#include "TScore.h"
// sound
#include "CWaveLoader.h"
#include "CSoundMixer.h"


_LIT(KFileStore,"save.dat");
// construct and destruct

// NewL()
CEngine* CEngine::NewL( CGameTetris* aGame, CWindowGc& aGc, RWindow& aWindow, TDisplayMode aDisplayMode )
{
	CEngine* self = new( ELeave )CEngine(aGame, aGc, aWindow, aDisplayMode );
	CleanupStack::PushL( self );
	self->ConstructL();
	CleanupStack::Pop( self );
	return self;
}

// ~
CEngine::~CEngine()
{
	if(iTimer)
	{
		iTimer->Cancel();
		delete iTimer;
	}

	delete iBackBufferGc;
	delete iBackBufferDevice;
	delete iBackBufferBitmap; 
	delete iMenu;
	delete iBitmapUtil;

	delete iScoreTable;

	// sound
	delete iSound;
	delete iSoundBg.iData;
	delete iSoundGameOver.iData;
	delete iSoundOnClick.iData;
	delete iSoundMenu.iData;
	delete iSoundClearRow.iData;
	delete iSoundWellDone.iData;
	delete iSoundLevelUp.iData;
}

// ConstructL()
void CEngine::ConstructL()
{
	// get the draw region
	TPoint pos = iWindow.Position();
	TSize size = iWindow.Size();
	iDrawRect.SetRect(pos, size);

	// Create a new bitmap with size of view’s rect and color depth of
	// screen
	iBackBufferBitmap = new(ELeave) CFbsBitmap();
	User::LeaveIfError(iBackBufferBitmap->Create(iDrawRect.Size(), iDisplayMode));
	
	// Create bitmap device for the bitmap
	iBackBufferDevice = CFbsBitmapDevice::NewL(iBackBufferBitmap);
	
	// Create graphics context for the bitmap
	User::LeaveIfError(iBackBufferDevice->CreateContext(iBackBufferGc));

	iBitmapUtil = new(ELeave)TBitmapUtil(iBackBufferBitmap);
	// Create a menu
	iMenu = CMenu::NewL(iBackBufferGc);

	iTimer = CGameTimer::NewL(*this);
	iPause = ETrue;

	// Load sound
	CWavLoader* wavLoader = CWavLoader::NewLC();

	iSoundBg		= wavLoader->LoadL(_L("bg.wav"));
	iSoundGameOver	= wavLoader->LoadL(_L("gameover.wav"));
	iSoundOnClick	= wavLoader->LoadL(_L("click.wav"));
	iSoundMenu		= wavLoader->LoadL(_L("menu.wav"));
	iSoundClearRow	= wavLoader->LoadL(_L("cleanrow.wav"));
	iSoundLevelUp	= wavLoader->LoadL(_L("levelup.wav"));
	iSoundWellDone	= wavLoader->LoadL(_L("welldone.wav"));

	CleanupStack::PopAndDestroy( wavLoader );

	// create sound mixer
	iSound = CSoundMixer::NewL();

	// make iSoundBg repeat itself
	iSoundBg.iRepEnd = iSoundBg.iLength;

	//iSound->Play(iSoundBg, 0, 16000, 256);
	
	
	// create score table
	iScoreTable = new (ELeave) CArrayFixSeg<TScore>(1);
	ScoreLoadL();

	iGameState = ENewSplash;
	//iGameState = EMenu;
}

// CEngine()
CEngine::CEngine( CGameTetris* aGame, CWindowGc& aGc, RWindow& aWindow, TDisplayMode aDisplayMode )
			: iGame(aGame)
			, iGc( aGc )
			, iWindow( aWindow )
			, iDisplayMode( aDisplayMode )
{}

///////////////////////////////////////////////////////////////////////////////////
// Other Method 

// DoGameFrameL()
TInt CEngine::DoGameFrameL()
{
	// Prepare to Splash
	if(iGameState == ENewSplash)
	{
		if(!ShowSplash())
			iGameState = EMenu;
	}

	if(iGameState == EExit)
	{
		if(!ShowSplash())
		{
			CTetrisAppUi* appUi = static_cast<CTetrisAppUi*> (CEikonEnv::Static()->AppUi());
			appUi->Quit();
			//User::Exit(0);	// this cann't check memory leak ,why create it ,i'm confused!
		}
	}

	// Prepare for showing the menu, used for state changing.
	if(iGameState == EMenu)
	{
		// Stop bg music
		iSound->Stop(0);
		// set Menu running frequency
		iTimer->SetTickTime(40000);
		iGameState = EMenuRunning;
	}

	// Menu running
	if(iGameState == EMenuRunning)
	{	
		// show main menu
		TInt32 signal = iMenu->Command(iKey);

		switch(signal)
		{
			case 1 :	// quit
			{
				iGameState = EExit;
			}break;
			case 2 :	// Start new game
			{
				if(!iMusicOff)
					iSound->Play(iSoundBg, 0, 16000, 64);
				iGameState = ERunning;
				iGame->iStateRunning = 0;
				iTimer->SetTickTime(50000);
			}break;
			case 3 :	// continue game
			{
				if(!iMusicOff)
					iSound->Play(iSoundBg, 0, 16000, 64);
				if(!iGame->iStateRunning)
					return 1;
				iGameState = ERunning;
				iTimer->SetTickTime(50000);
			}break;
			case 4:
			{
				iDisplayTop5 = 1;
			}break;
			case 5:
			{
				iDisplayTop5 = 0;
			}break;
			case 11 :	// Set brick rotation
			{
				if(iMenu->iSetting[0] == 0)
 					iGame->iStateRotateDir = -1;
				else
					iGame->iStateRotateDir = 1;
			}break;
			case 12 :	// Set Sound
			{
				if(iMenu->iSetting[1] == 2)		// set sound effect
					iSoundEffectOff = 0;
				else
					iSoundEffectOff = 1;
				if(iMenu->iSetting[2] == 4)		// set background music
					iMusicOff = 0;
				else
					iMusicOff = 1;
				iSound->SetVolume( (iMenu->iSetting[3] - 5) * 256 / 4 );
			}break;
			case 13 :	// Set Difficult
			{
				// start lines, scratching
				iGame->iStateStartLines = static_cast<TUint8> ((iMenu->iSetting[4] - 10) * 5);
				// Difficulty
				iGame->iStateDifficulty = static_cast<TUint8> (iMenu->iSetting[5] - 14);	
			}break;


			default:
			{
				iGc.Activate( iWindow );
				iMenu->Draw();
				iGc.BitBlt(iDrawRect.iTl, iBackBufferBitmap);
				if(iDisplayTop5)
					ScoreDisplay();
				iGc.Deactivate();
			}break;
		}
		
		iKey = 0;
	}
	
	// game over
	if(iGameState == EGameOver)
	{
		// show Txt "game over" and some other effects
		// check if it is a new high score
		// if it is, then prompt to input user's name
		// else goto main menu directly

		iBitmapUtil->Begin(TPoint(0, 0));		// lock the heap

		TUint16* addr = (TUint16*)iBackBufferBitmap->DataAddress();

		// width and height in pixel 
		TInt width = iBackBufferBitmap->SizeInPixels().iWidth;
		TInt height = iBackBufferBitmap->SizeInPixels().iHeight;
	
		if(!iControl)
			DipBlackwhite(addr, width, height);
		if(++iControl > 15)
			DipEffect(addr, width, height);
		iBitmapUtil->End();						// unlock the heap
		
		if(iControl == 35)
		{
			iControl = 0;	
			
			iGameState = EMenu;

			ScoreAddL();

			return 1;
		}

		iGc.Activate( iWindow );
		iGc.BitBlt(TPoint(0, 0), iBackBufferBitmap);
		
		const CFont* font;
		font = CEikonEnv::Static()->LegendFont();
		iGc.UseFont(font);
		iGc.SetPenColor(KRgbRed);
		iGc.DrawText(_L("Game Over"), TPoint(65, 100));
		
		iGc.DiscardFont();
		iGc.Deactivate();

		iKey = 0;
	}

	// running
	if(iGameState == ERunning)
	{
		TInt8	state = iGame->Run();

		if(state == 1)	// game over
		{
			if(!iSoundEffectOff)
				iSound->Play(iSoundGameOver, 1, 16000, 64);
			iGameState = EGameOver;
			return 1;
		}
		// draw screen
		if(iGame->Command(iKey) || state == 2)
		{
			iGc.Activate( iWindow );
	
			iGame->Draw(iBackBufferGc);

			iGc.BitBlt(iDrawRect.iTl, iBackBufferBitmap);

			iGc.Deactivate();
		}
		iKey = 0;

		// play sound effect
		if(!iSoundEffectOff)
		{
			// only play one sound effect ,there is a priority
			if(iGame->iStateLevelUp)
			{
				iSound->Play(iSoundLevelUp, 1, 16000, 64);
				iGame->iStateLevelUp = 0;
				//return 1;
			}
			if(iGame->iStateWellDone)
			{
				iSound->Play(iSoundWellDone, 2, 16000, 64);
				iGame->iStateWellDone = 0;
			}
			if(iGame->iStateClearRow)
			{
				iSound->Play(iSoundClearRow, 3, 16000, 64);
				iGame->iStateClearRow = 0;
				//return 1;
			}
		}	
	}

	return ETrue;
}


// Start()
void CEngine::Start()
{
	if( !iPause ) 
		return;
	iPause = EFalse;
	iTimer->Start();
	iSound->Resume();
}

// Stop()
void CEngine::Stop()
{
	if( iPause ) 
		return;
	iPause = ETrue;
	iTimer->Cancel();
	iSound->Pause();
}

// Pause()
void CEngine::Pause()
{
	if(iPause)
		Start();
	else
		Stop();
}


// Command()
void CEngine::Command(TInt aCommand)
{
	if(aCommand == 'q')
	{
		if(iGameState == ERunning || iGameState == EMenuRunning)	
			iGameState = EMenu;	
		else
			return;
	}

	if(aCommand == '0' && iGameState == ERunning)
		Pause();

	iKey = aCommand;

	if(iSoundEffectOff)
		return;

	if(iGameState == EMenuRunning)
	{
		switch(iKey)
		{
			case EStdKeyDevice5:
			case '5':
			case 63557:
			{
				iSound->Play(iSoundOnClick, 2, 16000, 64);
			}break;
			
			case EKeyUpArrow :								// UP
			case '2' :
			case EKeyDownArrow :							// Down
			case '8' :
			case EKeyLeftArrow :							// Left
			case '4' :
			case EKeyRightArrow :							// Right
			case '6' :
			{
				iSound->Play(iSoundMenu, 3, 16000, 64);
			}break;

			default:
				break;
		}
	}
}

// ShowSplash()
TBool CEngine::ShowSplash()
{
	if(!iSplash)
	{
		iSplash = CSplash::NewL(iGc, iWindow);

		iTimer->SetTickTime(100000);
	}
	else if(!iSplash->Draw())
	{
		delete iSplash;
		iSplash = NULL;
		return 0;			// stop splashing
	}
	return 1;				// keep splashing
}

// DipEffect()
void CEngine::DipEffect(TUint16* aAddr, TInt aWidthInPixel, TInt aHeightInPixel)
{
	TInt i, j;
	TUint16* index;

	for(i = 0; i < aHeightInPixel; i++)
	{
		index = aAddr + aWidthInPixel * i;		// get address of each row
		for(j = 0; j < aWidthInPixel; j++)
		{
			TInt8 r = static_cast<TInt8> (*index  & 0x00f);
            TInt8 g = static_cast<TInt8> ((*index & 0x0f0) >> 4);
            TInt8 b = static_cast<TInt8> ((*index & 0xf00) >> 8);

			r--;
			g--;
			b--;
			
			(r < 0) ? *index  = 0     : *index  = r;
            (g < 0) ? *index &= 0xf0f : *index |= g << 4;
            (b < 0) ? *index &= 0x0ff : *index |= b << 8;

			//(r > 0xf) ? *index  = 0xf   : *index  = r;
            //(g > 0xf) ? *index |= 0x0f0 : *index |= g << 4;
            //(b > 0xf) ? *index |= 0xf00 : *index |= b << 8;

			index ++;
		}
	}
}


void CEngine::DipBlackwhite(TUint16* aAddr, TInt aWidthInPixel, TInt aHeightInPixel)
{
	TInt i, j;
	TUint16* index;
	for(i = 0; i < aHeightInPixel; i++)
	{
		index = aAddr + aWidthInPixel * i;		// get address of each row
		for(j = 0; j < aWidthInPixel; j++)
		{
			*index = *index > 0x800 ? 0xfff : 0;

			index ++;
		}
	}
}

// method deal with score

void CEngine::ScoreLoadL()
{
	// in my opinion, this can only be used in real handset, not emulator
	TFileName fileName( KFileStore );
	CompleteWithAppPath( fileName );

	if(!BaflUtils::FileExists(CEikonEnv::Static()->FsSession(), fileName))
	{
		ScoreSaveL();
		return;
	}
	CFileStore* store = CDirectFileStore::OpenLC(CEikonEnv::Static()->FsSession(), fileName, EFileRead);

	RStoreReadStream stream;
	stream.OpenLC(*store,store->Root());

 
	TScore sc;
	TInt i, count=stream.ReadInt32L();

	for(i = 0; i < count; i++)
	{
		stream >> sc;
		iScoreTable->AppendL(sc);
	}

	CleanupStack::PopAndDestroy(2);		// (store and stream)
}


void CEngine::ScoreAddL()
{
	// first clear score table
	ScoreReset();
	// load score
	ScoreLoadL();	
	
	// check if the score is a new Top5 score
 	if(iScoreTable->Count() == TopNum)	// not full
		if(iGame->iStateScore <= -iScoreTable->At(TopNum - 1).iValue)
			return;
	
	TBuf<15>	name;

	CAknTextQueryDialog* dialog = CAknTextQueryDialog::NewL(name);
	if(!dialog->ExecuteLD(R_INPUT_USERNAME_DIALOG))
		return;

	// new score
	TScore	newScore(iGame->iStateScore, name);
    // Insert the element in the Score table (using an Integer key on iValue)
    TKeyArrayFix byScore(_FOFF(TScore, iValue), ECmpTInt);
    iScoreTable->InsertIsqAllowDuplicatesL(newScore, byScore);
   
    // Remove the last player from the table if necessary and compress the table
    if(iScoreTable->Count() > TopNum)
        iScoreTable->Delete(TopNum);
    iScoreTable->Compress();

	// save the score
	ScoreSaveL();
}



void CEngine::ScoreSaveL()
{
	// in my opinion, this can only be used in real handset, not emulator
	TFileName fileName( KFileStore );
	CompleteWithAppPath( fileName );

	CFileStore* store = CDirectFileStore::ReplaceLC(CEikonEnv::Static()->FsSession(), fileName, EFileWrite);

	store->SetTypeL(KDirectFileStoreLayoutUid);

	// Create the hi-score stream
	RStoreWriteStream stream;
	TStreamId id = stream.CreateLC(*store);

	// Write the number of score table entries in the store
	TInt count= iScoreTable->Count();
	stream.WriteInt32L(count);

	// Then write each entry
	for(TInt i = 0; i < count; i++)
	{
		stream << iScoreTable->At(i);
	}

	// Commit the changes to the stream
	stream.CommitL();
	CleanupStack::PopAndDestroy();

	// Set the stream in the store and commit the store
	store->SetRootL(id);
	store->CommitL();
	CleanupStack::PopAndDestroy();
}

void CEngine::ScoreDisplay()
{
	// note this don't has iGc.Activate( iWindow ); & iGc.Deactivate();
	//iGc.Activate( iWindow );
	const CFont* font;
	font = CEikonEnv::Static()->TitleFont();
	iGc.UseFont(font);
	iGc.SetPenColor(KRgbWhite);
	iGc.DrawText(_L("Top 5"), TPoint(75, 30));

	for(TInt i = 0; i < iScoreTable->Count(); i++)
	{
		iGc.DrawText(iScoreTable->At(i).iName, TPoint(30, 60 + 30*i));
		TBuf16<10> score;
		score.Num(-iScoreTable->At(i).iValue);
		iGc.DrawText(score, TPoint(130, 60 + 30*i));
	}
		
	iGc.DiscardFont();
	//iGc.Deactivate();
}

void CEngine::ScoreReset()
{
	iScoreTable->Delete(0, iScoreTable->Count());
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -