📄 mopoidgameengine.cpp
字号:
/*
========================================================================
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 + -