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

📄 mopoidgameengine.cpp

📁 mopoid game symbian os application development
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
 ========================================================================
 Name        : MopoidGameEngine.cpp
 Author      : 
 Copyright   : 
 Description : The game-engine. Handles control of the gameflow.
 License     : 
 
 ========================================================================
 */
#include "MopoidGameengine.h"

// -----------------------------------------------------------------------
// Construct engine object  (private)
// -----------------------------------------------------------------------
CMopoidGameEngine::CMopoidGameEngine(CMopoidContainer* aOwningControl) :
	iKeyHandler(), iHaveFocus(ETrue), iOwningControl(aOwningControl)
	{
	}

// -----------------------------------------------------------------------
// Destroy Engine Object
// -----------------------------------------------------------------------
CMopoidGameEngine::~CMopoidGameEngine()
	{
	StopTimer();

	// Sound Manager
	DELETE_AND_NULL(iSoundManager);

	// Delete bitmaps
	DELETE_AND_NULL(iBackBufferBmp);
	DELETE_AND_NULL(iBackBufferBmpGc);
	DELETE_AND_NULL(iBackBufferBmpDrawingDevice);

	DELETE_AND_NULL(iGrid);
	DELETE_AND_NULL(iSpriteHandler);

	DELETE_AND_NULL(iSettings);

	iFs.Close();
	}

// -----------------------------------------------------------------------
// leaving construction methods
// -----------------------------------------------------------------------
CMopoidGameEngine* CMopoidGameEngine::NewL(CMopoidContainer* aOwningControl)
	{
	CMopoidGameEngine* self = CMopoidGameEngine::NewLC(aOwningControl);
	CleanupStack::Pop(self);
	return self;
	}

CMopoidGameEngine* CMopoidGameEngine::NewLC(CMopoidContainer* aOwningControl)
	{
	CMopoidGameEngine* self = new (ELeave) CMopoidGameEngine(aOwningControl);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

// -----------------------------------------------------------------------
// Construction leaving function (public)
// -----------------------------------------------------------------------
void CMopoidGameEngine::ConstructL()
	{
	User::LeaveIfError(iFs.Connect());

	// Create settings object
	iSettings = CMopoidSettings::NewL();
	// Share the pointer to the settings object
	// with the utility classes that manage
	// the ball and the panel.
	iBall.SetSettingsPointer(iSettings);
	iPanel.SetSettingsPointer(iSettings);

	// The grid manages the contents of the current level.
	iGrid = new (ELeave) CMopoidGrid();
	iGrid->SetSettingsPointer(iSettings);

	// Reset the time
	iLastScoreDecreaseTime.UniversalTime();
	iLastFrameTime.UniversalTime();

	// Initialize graphics
	iSpriteHandler = CSpriteHandler::NewL(iFs);

	// Load high score and settings
	TRAPD(err, LoadGameProgressL())
	;
	if (err != KErrNone && err != KErrNotFound)
		User::Leave(err);

	// Load Sounds
	LoadSoundsL();

	// Do not start the game here - StartNewGame() has to be called
	// after the size of the screen has been sent to the game engine!
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::SetScreenSize(const TSize& aNewSize)
	{
	// Save the screen size in the settings object
	iSettings->SetScreenSize(aNewSize);

	// (re)create the back buffer bitmap with the new size
	// We can't leave in this method.
	// -> Ignore the error. Might not be the best solution of course,
	// but shouldn't be a big problem.
	TRAPD(err, CreateBackBufferL(aNewSize))
	;

	// Calculate the maximum drawing size that still leaves
	// room for the text.
	TSize gameDrawMaxSize(aNewSize);
	// For information text etc. 
	TInt textHeight = 2 * iSettings->iTextYOffset;
	gameDrawMaxSize.iHeight -= textHeight;

	// The game is currently optimized for portrait mode, therefore
	// the aspect ratio always has to be kept.
	// It would be possible to distort the image to adapt to landscape mode,
	// but this would result in a non-circular ball.
	// Otherwise, the level design would have to be adapted.
	const TReal gameAspectRatio = iSettings->iGameRectWidth
			/ iSettings->iGameRectHeight;

	// Calculate the game board size based on the maximum available
	// space (gameDrawMaxSize), but while still keeping the
	// aspect ratio.
	TSize gameDrawSize(gameDrawMaxSize);
	gameDrawSize.iHeight = (TInt)((TReal)gameDrawMaxSize.iWidth
			/ gameAspectRatio);
	if (gameDrawSize.iHeight > gameDrawMaxSize.iHeight)
		{
		// Reset the height to the screen height
		gameDrawSize.iHeight = gameDrawMaxSize.iHeight;
		// And calculate a new width instead
		gameDrawSize.iWidth = (TInt)((TReal)gameDrawMaxSize.iHeight
				* gameAspectRatio);
		}

	// Center the drawing rectangle on the screen in case it doesn't
	// use the full width (e.g. in landscape mode)
	const TPoint topLeftOffset((aNewSize.iWidth - gameDrawSize.iWidth) / 2,
			aNewSize.iHeight - gameDrawSize.iHeight);
	iSettings->SetDrawRect(TRect(topLeftOffset, gameDrawSize));

	// Set sizes of elements
	// Calculate the ratio of the virtual (internal, full-resolution) play field
	// and the game field that'll be drawn
	const TReal widthRatio = (TReal)(iSettings->iDrawRect.Width())
			/ (TReal)(iSettings->iGameRect.Width());
	const TReal heightRatio = (TReal)(iSettings->iDrawRect.Height())
			/ (TReal)(iSettings->iGameRect.Height());

	// Ball
	iSpriteHandler->SetSpriteSize(MopoidShared::ESpriteBall, TSize(
			(TInt)( (TReal)iSettings->iBallSize.iWidth * widthRatio ),
			(TInt)( (TReal)iSettings->iBallSize.iHeight * heightRatio ) ));

	// Panel
	iSpriteHandler->SetSpriteSize(MopoidShared::ESpritePanel, TSize(
			(TInt)((TReal)iSettings->iPanelSize.iWidth * widthRatio),
			(TInt)((TReal)iSettings->iPanelSize.iHeight * heightRatio)));

	// Grid-Bricks
	iSpriteHandler->SetSpriteSize(MopoidShared::ESpriteBrickNormal, TSize(
			(TInt)((TReal)iSettings->iBrickSize.iWidth * widthRatio),
			(TInt)((TReal)iSettings->iBrickSize.iHeight * heightRatio)));
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::DeleteBackBuffer()
	{
	DELETE_AND_NULL(iBackBufferBmpDrawingDevice);
	DELETE_AND_NULL(iBackBufferBmpGc);
	DELETE_AND_NULL(iBackBufferBmp);
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::CreateBackBufferL(const TSize& aSize)
	{
	// First remove the old back buffer if it already exists
	DeleteBackBuffer();
	iBackBufferBmp = new (ELeave) CFbsBitmap;
	// Create a new back buffer with 16 million colors + alpha transparency
	// Should be the fastest mode in most caes, according to the
	// "Games on Symbian OS"-book, as it doesn't have to be converted
	// when drawing to the screen.
	User::LeaveIfError(iBackBufferBmp->Create(aSize, EColor16MA));
	iBackBufferBmpDrawingDevice = CFbsBitmapDevice::NewL(iBackBufferBmp);
	//create graphics context for the bitmap
	User::LeaveIfError(iBackBufferBmpDrawingDevice->CreateContext(iBackBufferBmpGc) );
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::LoadSoundsL()
	{
	iSoundManager = new (ELeave) CSoundManager();

	// Load the two sounds that are used by the game
	_LIT(KSnd1, "hit.wav");
	iSoundManager->LoadSoundFileL(iFs, KSnd1, ESoundHit);
	_LIT(KSnd2, "bounce.wav");
	iSoundManager->LoadSoundFileL(iFs, KSnd2, ESoundBounce);

	// Apply the volume settings.
	iSoundManager->SetVolume(iSettings->iSoundLevel);
	}

//------------------------------------------------------------------------------
TBool CMopoidGameEngine::IsSoundOn()
	{
	return (iSettings->iSoundLevel > 0);
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::SetSoundLevelL(TBool aOnOff)
	{
	// Choose the sound level
	iSettings->iSoundLevel = (aOnOff) ? VOLUME_LOW : VOLUME_OFF;

	iSoundManager->SetVolume(iSettings->iSoundLevel);

	// Save the configuration changes immediately
	SaveGameProgressL();
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::StartNewGameL()
	{
	iSettings->iGameStatus = CMopoidSettings::EStarted;
	iSettings->iLevel = 1;
	iSettings->iLives = 3;
	iSettings->iScore = 0;
	iLevels.LoadLevelL(iFs, (iSettings->iLevel), iGrid, iSettings);
	SetScreenSize(iSettings->iScreenSize);

	ResetBallAndPanel();

	iLastFrameTime.UniversalTime();
	iLastScoreDecreaseTime.UniversalTime();

	// Update our back buffer bitmap so that it shows the new scene and
	// status message
	DoFrame();
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::ResetBallAndPanel()
	{
	// Set ball
	iBall.iXnew = iSettings->iGameRect.iTl.iX + (iSettings->iGameRect.Width()
			/ 2);
	iBall.iYnew = iSettings->iGameRect.iBr.iY - iSettings->iPanelSize.iHeight
			- iSettings->iBallSize.iHeight;
	TReal maxBallStartSpeedX = iSettings->iBallSpeedMaxX / 4.0;
	if (maxBallStartSpeedX > iSettings->iBallStartSpeed / 2.0)
		{
		// Make sure the horizontal speed doesn't get higher than the 
		// max. allowed start speed - otherwise, the ball would not move up.
		maxBallStartSpeedX = iSettings->iBallStartSpeed / 2.0;
		}
	// We get a random value from -0.5 ... 0.5 with this method - so multiply
	// max ball speed x with 2.0 in order to use the full value (in an absolute
	// number sense, e.g. for both positive and negative speeds)
	iBall.iVx = (Math::FRand(iSettings->iRandSeed) - 0.5) * maxBallStartSpeedX
			* 2.0;
	Math::Sqrt(iBall.iVy, iSettings->iBallStartSpeed
			* iSettings->iBallStartSpeed - iBall.iVx * iBall.iVx);
	iBall.iVy = -iBall.iVy;
	iBall.ConvertToPos();

	// Set panel
	const TInt newPanelX = iSettings->iGameRect.iTl.iX
			+ (iSettings->iGameRect.Width() / 2)
			- iSettings->iPanelSizeHalf.iWidth;
	iPanel.iPos = TPoint(newPanelX, iSettings->iGameRect.iBr.iY
			- iSettings->iPanelSize.iHeight);
	iPanel.iX = newPanelX;
	}

// -----------------------------------------------------------------------
void CMopoidGameEngine::StartTimerL()
	{
	// TODO: Define the tick interval
	// in microseconds: 5000 equals to 0,005 seconds
	const TTimeIntervalMicroSeconds32 KTickInterval = 5000;

	// TODO: Create the timer if necessary
	if (!iPeriodicTimer)
		{
		iPeriodicTimer = CPeriodic::NewL(CActive::EPriorityStandard);
		}
	// TODO: Start the timer with the tick interval that you defined.
	// Use CMopoidGameEngine::TimerCallBack for the function pointer
	// of the callback object and "this" for the instance pointer that
	// will be sent to the callback function.
	iPeriodicTimer->Start(KTickInterval, KTickInterval, TCallBack(
			TimerCallBack, this));
	}

//------------------------------------------------------------------------------
void CMopoidGameEngine::StopTimer()
	{
	// If the timer object exists: stop and delete it.
	// TODO: Check if the timer exists and cancel it.
	// Also delete the timer instance and set the pointer
	// to NULL.
	if (iPeriodicTimer)
		{
		iPeriodicTimer->Cancel();
		delete iPeriodicTimer;
		iPeriodicTimer = NULL;
		}
	}

//------------------------------------------------------------------------------
TInt CMopoidGameEngine::TimerCallBack(TAny* aObject)
	{
	// Call non-static method
	return ((CMopoidGameEngine*)aObject)->DoFrame();

	}

// -----------------------------------------------------------------------
TBool CMopoidGameEngine::DoFrame()
	{
	// Check how much time has passed since we calculated the last frame
	CalcTimeDifference();

	// Decrease score every x seconds
	if (iLastFrameTime.MicroSecondsFrom(iLastScoreDecreaseTime)
			> iSettings->iScoreDecreaseTime)
		{
		// Decrease score
		iSettings->iScore -= iSettings->iScoreDecrement;
		// TODO: Refresh back light timer
		User::ResetInactivityTime();
		// Update last setting time
		iLastScoreDecreaseTime.UniversalTime();
		}

	// Animate panel
	AnimatePanel();

	// Animate ball
	TRAPD(err, AnimateBallL())
	;

	// Update back buffer
	DrawFrame();

	// Send event to system that it should do a redraw
	iOwningControl->DrawNow();

	// Only continue when we're playing.
	if (iSettings->iGamePaused || iSettings->iGameStatus
			!= CMopoidSettings::EPlaying)
		{
		// Make sure that pause is activated, in case we're displaying a new level message.
		iSettings->iGamePaused = ETrue;
		return EFalse;
		}

	// Send back that we want to be called again.
	return ETrue;
	}

// -----------------------------------------------------------------------
void CMopoidGameEngine::CalcTimeDifference()
	{
	TTime curFrameTime;
	curFrameTime.UniversalTime();
	TTimeIntervalMicroSeconds tStepMicroS =
			curFrameTime.MicroSecondsFrom(iLastFrameTime);
	iThisFrameStep = ((TReal)(tStepMicroS.Int64())) / 100000.0;

	// Security check if the pause between two frames was too long - limit it
	// so that the ball can't jump around too much and pass through bricks.
	if (iThisFrameStep > 1.0)
		iThisFrameStep = 1.0;

	iLastFrameTime = curFrameTime;
	}

// -----------------------------------------------------------------------
void CMopoidGameEngine::AnimatePanel()
	{
	iPanel.KeyboardMove(iKeyHandler.GetDirection());

	// Move the panel according to its speed.
	iPanel.ConvertToPos(iThisFrameStep);
	}

// -----------------------------------------------------------------------
void CMopoidGameEngine::AnimateBallL()
	{

⌨️ 快捷键说明

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