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

📄 gamemanager.cpp

📁 分布式坦克游戏
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*****************************************************************************
*                                                                             
*   GameManager.cpp                                                            
*                                                                             
*   Electrical Engineering Faculty - Software Lab                             
*   Spring semester 1998                                                      
*                                                                             
*   Tanks game                                                                
*                                                                             
*   Module description: The core of the game - implements the main game loop.
*                       Holds the list of objects participating in the game.
*                       In every game iteration, all the objects on the list
*                       are refreshed, and the game display is updated.
*                       More details on the algorithms in use are described below.
*                                                                             
*   Authors: Eran Yariv - 28484475                                           
*            Moshe Zur  - 24070856                                           
*                                                                            
*                                                                            
*   Date: 23/09/98                                                           
*                                                                            
******************************************************************************/
#include "stdafx.h"
#include <afxmt.h>
#include "tanks.h"
#include "DIB.h"
#include "GameManager.h"
#include "ImageManager.h"
#include "MsgQueue.h"
#include "Bonus.h"
#include "GameBoard.h"
#include "TankObj.h"
#include "Shell.h"
#include "Bullet.h"
#include "Mine.h"
#include "Bomber.h"
#include "Message.h"
#include "GameOver.h"


CGameManager::CGameManager (UINT uFreq) :
        m_Timer(TANKS_APP->m_gTimer),
        m_ImageManager(TANKS_APP->m_gImageManager),
        m_MsgQueue(TANKS_APP->m_gIncomingMsgQueue),
        m_CommManager(TANKS_APP->m_gCommManager),
        m_dwBonusTimeout (0)
{
    VERIFY (SetFrequency (uFreq));
}

BOOL CGameManager::SetFrequency (UINT uFreq)
{
    if (uFreq < MIN_RENDER_FREQ || uFreq > MAX_RENDER_FREQ)
        return FALSE;
    m_uFreq = uFreq;
    m_uMilliSleep = UINT (double(1000.0) / double(m_uFreq));
    return TRUE;
}

UINT CGameManager::ThreadEntry (LPVOID /*lpParam*/)
{
    SetPriority (GAME_MANAGER_THREAD_PRIORITY);
    m_MapHWnd = TANKS_APP->m_gHwndMap;              // Get handle to map (Windows handle)
	ASSERT (NULL != m_MapHWnd);
    m_dwChkSumSignal = 0;                           // Don't send check sum right away
    m_iNumTanks = 0;                                // Initially, no tanks.
    m_iLocalTankID = -1;                            // Initially, no local tank

    HDC dc = ::GetDC (m_MapHWnd);
    m_BackBuffer.CreateEmpty (MAP_WIDTH, MAP_HEIGHT);// Create back (off-screen) buffer
    m_BackBuffer.GetPaletteFromResourceBitmap (IDB_BULLET);
    TANKS_APP->m_gDrawDIB.SetPalette (*m_BackBuffer.m_pPalette);
    TANKS_APP->m_gDrawDIB.Realize (dc, FALSE);
    ::ReleaseDC (m_MapHWnd, dc);

    MultiRectGameTechnique ();                      // Do the game main loop
    DestroyObjects ();                              // Kill list of objects
    return 0;
}

void CGameManager::DestroyObjects ()
{
    m_GameObjsList.KillList();

    m_TanksCS.Lock();
    for (int i=0; i<MAX_TANKS; i++)
        m_pTanks[i] = NULL;
    m_TanksCS.Unlock();

    m_BonusCS.Lock();
    m_pBonus = NULL;
    m_BonusCS.Unlock();

    m_iNumTanks = 0;
}

#pragma warning (disable : 4701)
/* warning C4701: local variable 'CurObjHImg' may be used without having been initialized
   God damn it, trust me on this one - I know what I'm doing here....
*/

/*------------------------------------------------------------------------------

  Function: MultiRectGameTechnique

  Purpose:  Main game loop. Every iteration of the loop, game objects recalc
            their status, and the display is refreshed. Every Iteration lasts a 
            constant period of time, to allow a steady frame rate of the display, 
            using the sleep system call, to stay low on CPU use.

  Input:    None.

  Output:   None.

  Remarks:  The algorithm in use is explained in details in the Programmers's
            manual section in the game's help files.
            

------------------------------------------------------------------------------*/
void 
CGameManager::MultiRectGameTechnique ()
{
#ifdef GATHER_RENDERING_STATS
    DWORD dwLoopCount=0;
    DWORD dwStartTime = GetTickCount();
    int lTotalTimeLeft = 0,                 // For statistics only !
        lMaxTimeLeft = 0,                   // For statistics only !
        lMinTimeLeft = LONG(m_uMilliSleep), // For statistics only !
        iMaxObjs = 0;                       // For statistics only !
#endif  // GATHER_RENDERING_STATS
    BOOL bImageChanged;

    typedef struct {
        HIMAGE      himg;
        CPoint      ObjectPos;
    } UpdateArrayUnit;

    UpdateArrayUnit UpdateArray[MAX_POSSIBLE_OBJECTS];

    UINT uArrIndex;

    LONG lSleepTime;         // Time left to sleep (may be negative if loop is too slow)

    m_bRefreshAll = TRUE;

        // Initially, empty the message queue to add the board object to the game objects list
    EmptyMsgQ(dwStartTime);
        // First, create the constant board in the back-buffer
    LIST_POS lp = m_GameObjsList.GetHeadPosition();
    CGameObject* pBoard = m_GameObjsList.GetNext(lp);
    ASSERT (pBoard->GetType() == BOARD);
    CDIB *pBoardDIB = m_ImageManager.ExposeDIB (pBoard->GetImage());
        // Copy the board directly - no transparency
    m_BackBuffer.CopyFrom (pBoardDIB);
    while (!m_bEndThread)
    {   // Loop while game lasts
            // Get sample time
        DWORD dwCurTime = m_Timer.GetLocalTime();
        DWORD dwLoopStart = dwCurTime;

        AttemptToSendGameChecksum ();

        if (m_CommManager.IsHost())
            TryToAddBonus (dwCurTime);

            // Empty object list requests queue
        bImageChanged = EmptyMsgQ(dwCurTime);

            // Init update rectangle
        m_UpdateRegionRect.SetRectEmpty();

            // Init updates array
        uArrIndex = 0;

        //
        // First pass: loop the objects 
        //

            // Walk over game objects and update rectangle
            // Skip the board:
        LIST_POS lp = m_GameObjsList.GetHeadPosition();
        CGameObject* pGameObj = m_GameObjsList.GetNext(lp);
        ASSERT (pGameObj->GetType() == BOARD);

            // Loop the other (transparent non-board) objects:
        for ( pGameObj = m_GameObjsList.GetNext(lp);
              pGameObj;
              pGameObj = m_GameObjsList.GetNext(lp) )
        {
            StateType CurObjState = pGameObj->CalcState(dwCurTime);
            HIMAGE CurObjHImg;

                // Get new position from current object
            CPoint CurObjPos = pGameObj->GetPos();

                // Get update rectangle from current object
            CRect CurObjUpdateRect = pGameObj -> GetUpdateRectangle();
                // Update region to be union of all rectangles
            m_UpdateRegionRect |= CurObjUpdateRect;

            if (STATE_DEAD == CurObjState)
            {       // Object is no longer with us:
                BOOL fIsMine = FALSE;
                if (pGameObj->GetType() == MINE)
                {
                    fIsMine = TRUE;
                }
                else if (pGameObj->GetType() == TANK)
                {
                    m_TanksCS.Lock();
                    // A tank is dying, remove it from fast pointer array
                    ASSERT (NULL != m_pTanks[pGameObj->GetID()]);   // Tank must still exist
                    int iID = pGameObj->GetID();
                    if (m_iLocalTankID == iID)
                    {   // Local tank is removed
                        m_CommManager.NotifyExplodingTank(m_iLocalTankID);
                        m_iLocalTankID = -1;
                    }
                    m_pTanks[iID] = NULL;
                    m_iNumTanks --;
                    ASSERT (m_iNumTanks >= 0);
                    m_TanksCS.Unlock();
                }
                else if (pGameObj->GetType() == BONUS)
                {
                    m_BonusCS.Lock();
                    // A bonus is dying, remove it from fast pointer
                    if (m_pBonus == pGameObj)
                        // If the current bonus is dying (not a newer one)
                        m_pBonus = NULL;
                    m_BonusCS.Unlock();
                }
                if (fIsMine)
                    m_MinesCS.Lock();
                m_GameObjsList.RemoveObject(pGameObj);
                if (fIsMine)
                    m_MinesCS.Unlock();
                    // If object is removed, map image has changed    
                bImageChanged = TRUE;
            } 
            else 
            {       // STATE_ALIVE: Update update rect size:
                CurObjHImg = pGameObj->GetImage();  // This call can change the bImageChanged of the object
                if (!bImageChanged) 
                        // No change detected yet....
                        // Ask the current object if it changed image
                    bImageChanged = pGameObj->HasImageChanged();
            }
                // Clear back-buffer from previous frame 
            m_BackBuffer.CopyRectFrom (pBoardDIB, 
                                       CurObjUpdateRect.left, 
                                       CurObjUpdateRect.top, 
                                       CurObjUpdateRect.Width(), 
                                       CurObjUpdateRect.Height(), 
                                       CurObjUpdateRect.left, 
                                       CurObjUpdateRect.top);
                // If the object is still alive, add it to the array:
            if (CurObjState == STATE_ALIVE)
            {
                ASSERT (uArrIndex < MAX_POSSIBLE_OBJECTS);
                UpdateArray[uArrIndex].himg = CurObjHImg;
                UpdateArray[uArrIndex].ObjectPos.x = CurObjPos.x;
                UpdateArray[uArrIndex++].ObjectPos.y = CurObjPos.y;
            } 
        }
        
#ifdef GATHER_RENDERING_STATS
        iMaxObjs = max (iMaxObjs, (int)uArrIndex);  // For statistics only!
#endif  // GATHER_RENDERING_STATS

        //
        // Second pass, clear the array
        //

        if (bImageChanged || m_bRefreshAll)
        {
            // The game image has changed - m_UpdateRegionRect holds the change rectangle
            for (UINT i = 0; i < uArrIndex; i++)
                // While there are objects to update in the array
            {
                    // Update back buffer:
                m_ImageManager.DisplayImage (UpdateArray[i].himg, 
                                             &m_BackBuffer, 
                                             UpdateArray[i].ObjectPos);
            }

            if (m_bRefreshAll) 
            {   // First time around ?
                m_bRefreshAll = FALSE; // No more !
                    // Update entire map to display all terrain
                m_UpdateRegionRect.SetRect (0,0, MAP_WIDTH , MAP_HEIGHT);
            }
            // Dump it (flip)
            DumpBackBufferToScreen ();
        }

        lSleepTime = LONG(m_uMilliSleep) - LONG(m_Timer.GetLocalTime() - LONG(dwLoopStart));

#ifdef GATHER_RENDERING_STATS
            // Update counters, timers and statistics:
        dwLoopCount++;
        lTotalTimeLeft += lSleepTime;                    // For statistics only !
        lMaxTimeLeft = max (lMaxTimeLeft, lSleepTime);   // For statistics only !
        lMinTimeLeft = min (lMinTimeLeft, lSleepTime);   // For statistics only !
#endif

        if (lSleepTime > 1)  // Do we need to sleep at all?
            Sleep (lSleepTime);
    }   // End of main rendering loop


#ifdef GATHER_RENDERING_STATS
        // Debug performance dump:
    TRACE ( "\n\t\t\t**** Rendering statistics ****\n");
    TRACE ("%d loops per second\n",
            dwLoopCount / ((GetTickCount() - dwStartTime) / 1000));
    TRACE ("Avg. time left in loop = %f millisecs. Min = %d millisecs, Max = %d millisecs\n",
            double(lTotalTimeLeft) / double(dwLoopCount), lMinTimeLeft, lMaxTimeLeft);
    TRACE ("Max concurrent objects = %d\n\n\n", iMaxObjs);
#endif
}

#pragma warning (default : 4701)

    
/*------------------------------------------------------------------------------

  Function: DumpBackBufferToScreen

  Purpose:  Switched between the back buffer and the front buffer.

  Input:    None.

  Output:   None.

  Remarks:  See game manager algorithm for details.

------------------------------------------------------------------------------*/
void CGameManager::DumpBackBufferToScreen ()
{
    HDC dc = ::GetDC (m_MapHWnd);

    int iWidth  = m_UpdateRegionRect.Width(),

⌨️ 快捷键说明

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