<html>
绚丽的大型游戏背后,都存在着一个主循环,能依据游戏的进度控制游戏的状态。主循环管理游戏的正常执行。管理着游戏执行失败该怎样处理,对某些不能执行的情况给予杀死等等。
另外。在MMORPG中,主循环相同也是AI的管理者。时刻依据游戏的执行状态,调度出不同的AI执行,增强了游戏的可玩性和趣味性。同一时候主循环对于关卡控制。游戏画面渲染,声音管理。资源管理也有所掌控。总之,主循环对于大型游戏是不可或缺的一部分~~
下面是游戏主循环的各个组成部分:
I. 游戏初始化的相关參数:
#pragma once #include <iostream> using namespace std; //======================================================================== // Initialization.h : Defines utility functions for game initialization //======================================================================== typedef unsigned int DWORD; typedef long long DWORDLONG; typedef unsigned char TCHAR; extern bool CheckStorage(const DWORDLONG diskSpaceNeeded); extern DWORD ReadCPUSpeed(); extern bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded); extern bool IsOnlyInstance(const TCHAR* gameTitle); extern const TCHAR *GetSaveGameDirectory(HWND hWnd, const TCHAR *gameAppDirectory); extern bool CheckForJoystick(HWND hWnd); struct GameOptions { // Level option 与关卡相关的选项 std::string m_Level; // Rendering options 与渲染相关的选项 std::string m_Renderer; bool m_runFullSpeed; Point m_ScreenSize; // Sound options 与音频相关 float m_soundEffectsVolume; float m_musicVolume; // Multiplayer options 多人同一时候在线选项 int m_expectedPlayers; int m_listenPort; std::string m_gameHost; int m_numAIs; int m_maxAIs; int m_maxPlayers; // resource cache options 资源缓存 bool m_useDevelopmentDirectories; // TiXmlElement - look at this to find other options added by the developer TiXmlDocument *m_pDoc; GameOptions(); ~GameOptions() { SAFE_DELETE(m_pDoc); } void Init(const char* xmlFilePath, LPWSTR lpCmdLine); };
//======================================================================== // Initialization.cpp : Defines utility functions for game initialization //======================================================================== #include "GameCodeStd.h" #include <shlobj.h> #include <direct.h> #include "Initialization.h" // // CheckStorage 对于端游而言的安装. // bool CheckStorage(const DWORDLONG diskSpaceNeeded) { // Check for enough free disk space on the current disk. int const drive = _getdrive(); struct _diskfree_t diskfree; _getdiskfree(drive, &diskfree); unsigned __int64 const neededClusters = diskSpaceNeeded /(diskfree.sectors_per_cluster*diskfree.bytes_per_sector); if (diskfree.avail_clusters < neededClusters) { // if you get here you donít have enough disk space! GCC_ERROR("CheckStorage Failure: Not enough physical storage."); return false; } return true; } // // CheckMemory // bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded) { MEMORYSTATUSEX status; GlobalMemoryStatusEx(&status); if (status.ullTotalPhys < physicalRAMNeeded) { // you donít have enough physical memory. Tell the player to go get a real // computer and give this one to his mother. GCC_ERROR("CheckMemory Failure: Not enough physical memory."); return false; } // Check for enough free memory. if (status.ullAvailVirtual < virtualRAMNeeded) { // you donít have enough virtual memory available. // Tell the player to shut down the copy of Visual Studio running in the // background, or whatever seems to be sucking the memory dry. GCC_ERROR("CheckMemory Failure: Not enough virtual memory."); return false; } char *buff = GCC_NEW char[(unsigned int)virtualRAMNeeded]; if (buff) delete[] buff; else { // even though there is enough memory, it isnít available in one // block, which can be critical for games that manage their own memory GCC_ERROR("CheckMemory Failure: Not enough contiguous available memory."); return false; } return true; } // // ReadCPUSpeed // DWORD ReadCPUSpeed() { DWORD BufSize = sizeof(DWORD); DWORD dwMHz = 0; DWORD type = REG_DWORD; HKEY hKey; // open the key where the proc speed is hidden: long lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey); if(lError == ERROR_SUCCESS) { // query the key: RegQueryValueEx(hKey, L"~MHz", NULL, &type, (LPBYTE) &dwMHz, &BufSize); } return dwMHz; } /*** DWORD GetFreeVRAM() { // NOTE: This method is deprecated, and unfortunately not really replaced with // anything useful..... DDSCAPS2 ddsCaps; ZeroMemory(&ddsCaps, sizeof(ddsCaps)); ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; DWORD dwUsedVRAM = 0; DWORD dwTotal=0; DWORD dwFree=0; // lp_DD points to the IDirectDraw object HRESULT hr = g_pDisplay->GetDirectDraw()->GetAvailableVidMem(&ddsCaps, &dwTotal, &dwFree); // dwUsedVRAM holds the number of bytes of VRAM used dwUsedVRAM = dwTotal-dwFree; return dwUsedVRAM; return 0; } ****/ GameOptions::GameOptions() { // set all the options to decent default valu m_Level = ""; m_Renderer = "Direct3D 9"; m_runFullSpeed = false; m_soundEffectsVolume = 1.0f; m_musicVolume = 1.0f; m_expectedPlayers = 1; m_listenPort = -1; std::string m_gameHost = "MrMike-m1710"; m_numAIs = 1; m_maxAIs = 4; m_maxPlayers = 4; m_ScreenSize = Point(1024,768); m_useDevelopmentDirectories = false; m_pDoc = NULL; } void GameOptions::Init(const char* xmlFileName, const TCHAR* lpCmdLine) { // read the XML file // if needed, override the XML file with options passed in on the command line. m_pDoc = new TiXmlDocument(xmlFileName); if (m_pDoc && m_pDoc->LoadFile()) { TiXmlElement *pRoot = m_pDoc->RootElement(); if (!pRoot) return; // Loop through each child element and load the component TiXmlElement* pNode = NULL; pNode = pRoot->FirstChildElement("Graphics"); if (pNode) { std::string attribute; attribute = pNode->Attribute("renderer"); if (attribute != "Direct3D 9" && attribute != "Direct3D 11") { GCC_ASSERT(0 && "Bad Renderer setting in Graphics options."); } else { m_Renderer = attribute; } if (pNode->Attribute("width")) { m_ScreenSize.x = atoi(pNode->Attribute("width")); if (m_ScreenSize.x < 800) m_ScreenSize.x = 800; } if (pNode->Attribute("height")) { m_ScreenSize.y = atoi(pNode->Attribute("height")); if (m_ScreenSize.y < 600) m_ScreenSize.y = 600; } if (pNode->Attribute("runfullspeed")) { attribute = pNode->Attribute("runfullspeed"); m_runFullSpeed = (attribute == "yes") ? true : false; } } pNode = pRoot->FirstChildElement("Sound"); if (pNode) { m_musicVolume = atoi(pNode->Attribute("musicVolume")) / 100.0f; m_soundEffectsVolume = atoi(pNode->Attribute("sfxVolume")) / 100.0f; } pNode = pRoot->FirstChildElement("Multiplayer"); if (pNode) { m_expectedPlayers = atoi(pNode->Attribute("expectedPlayers")); m_numAIs = atoi(pNode->Attribute("numAIs")); m_maxAIs = atoi(pNode->Attribute("maxAIs")); m_maxPlayers = atoi(pNode->Attribute("maxPlayers")); m_listenPort = atoi(pNode->Attribute("listenPort")); m_gameHost = pNode->Attribute("gameHost"); } pNode = pRoot->FirstChildElement("ResCache"); if (pNode) { std::string attribute(pNode->Attribute("useDevelopmentDirectories")); m_useDevelopmentDirectories = ((attribute == "yes") ?以上就是主循环中游戏初始化相关的东西。包含声音。资源,游戏选项等。(true) : (false)); } } } // // IsOnlyInstance // bool IsOnlyInstance(const TCHAR* gameTitle) { // Find the window. If active, set and return false // Only one game instance may have this mutex at a time... HANDLE handle = CreateMutex(NULL, TRUE, gameTitle); // Does anyone else think 'ERROR_SUCCESS' is a bit of an oxymoron? if (GetLastError() != ERROR_SUCCESS) { HWND hWnd = FindWindow(gameTitle, NULL); if (hWnd) { // An instance of your game is already running. ShowWindow(hWnd, SW_SHOWNORMAL); SetFocus(hWnd); SetForegroundWindow(hWnd); SetActiveWindow(hWnd); return false; } } return true; } // // GetSaveGameDirectory // const TCHAR *GetSaveGameDirectory(HWND hWnd, const TCHAR *gameAppDirectory) { HRESULT hr; static TCHAR m_SaveGameDirectory[MAX_PATH]; TCHAR userDataPath[MAX_PATH]; hr = SHGetSpecialFolderPath(hWnd, userDataPath, CSIDL_APPDATA, true); _tcscpy_s(m_SaveGameDirectory, userDataPath); _tcscat_s(m_SaveGameDirectory, _T("\\")); _tcscat_s(m_SaveGameDirectory, gameAppDirectory); // Does our directory exist? if (0xffffffff == GetFileAttributes(m_SaveGameDirectory)) { if (SHCreateDirectoryEx(hWnd, m_SaveGameDirectory, NULL) != ERROR_SUCCESS) return false; } _tcscat_s(m_SaveGameDirectory, _T("\\")); return m_SaveGameDirectory; } // // bool CheckForJoystick 手柄检測 // bool CheckForJoystick(HWND hWnd) { JOYINFO joyinfo; UINT wNumDevs; BOOL bDev1Attached, bDev2Attached; if((wNumDevs = joyGetNumDevs()) == 0) return false; bDev1Attached = joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED; bDev2Attached = joyGetPos(JOYSTICKID2,&joyinfo) != JOYERR_UNPLUGGED; if(bDev1Attached) joySetCapture(hWnd, JOYSTICKID1, 1000/30, true); if (bDev2Attached) joySetCapture(hWnd, JOYSTICKID2, 1000/30, true); return true; }
II. 游戏过程 > GameProcess
#pragma once //======================================================================== // Process.h : defines common game events //======================================================================== #include <memory> #include <iostream> using namespace std::shared_ptr; using namespace std::weak_ptr; class Process; typedef shared_ptr<Process> StrongProcessPtr; typedef weak_ptr<Process> WeakProcessPtr; //-------------------------------------------------------------------- // Process class // // Processes are ended by one of three methods: Success, Failure, or Aborted. // - Success means the process completed successfully. If the process has a child, it will be attached to the process mgr. // - Failure means the process started but failed in some way. If the process has a child, it will be aborted. // - Aborted processes are processes that are canceled while not submitted to the process mgr. Depending on the circumstances, they may or may not have gotten an OnInit() call. // For example, a process can // spawn another process and call AttachToParent() on itself. If the new process fails, the child will get an Abort() call on it, even though its status is RUNNING. //-------------------------------------------------------------------- class Process { friend class ProcessManager; public: enum State { // Processes that are neither dead nor alive UNINITIALIZED = 0, // created but not running REMOVED, // removed from the process list but not destroyed; this can happen when a process that is already running is parented to another process // Living processes RUNNING, // initialized and running PAUSED, // initialized but paused // Dead processes SUCCEEDED, // completed successfully FAILED, // failed to complete ABORTED, // aborted; may not have started }; private: State m_state; // the current state of the process StrongProcessPtr m_pChild; // the child process, if any public: // construction Process(void); virtual ~Process(void); protected: // interface; these functions should be overridden by the subclass as needed virtual void VOnInit(void) { m_state = RUNNING; } // called during the first update; responsible for setting the initial state (typically RUNNING) virtual void VOnUpdate(unsigned long deltaMs) = 0; // called every frame virtual void VOnSuccess(void) { } // called if the process succeeds (see below) virtual void VOnFail(void) { } // called if the process fails (see below) virtual void VOnAbort(void) { } // called if the process is aborted (see below) public: // Functions for ending the process. inline void Succeed(void); inline void Fail(void); // pause inline void Pause(void); inline void UnPause(void); // accessors State GetState(void) const { return m_state; } bool IsAlive(void) const { return (m_state == RUNNING || m_state == PAUSED); } bool IsDead(void) const { return (m_state == SUCCEEDED || m_state == FAILED || m_state == ABORTED); } bool IsRemoved(void) const { return (m_state == REMOVED); } bool IsPaused(void) const { return m_state == PAUSED; } // child functions inline void AttachChild(StrongProcessPtr pChild); StrongProcessPtr RemoveChild(void); // releases ownership of the child StrongProcessPtr PeekChild(void) { return m_pChild; } // doesn't release ownership of the child private: void SetState(State newState) { m_state = newState; } }; //-------------------------------------------------------------------------------------------- // Inline function definitions //-------------------------------------------------------------------------------------------- inline void Process::Succeed(void) { GCC_ASSERT(m_state == RUNNING || m_state == PAUSED); m_state = SUCCEEDED; } inline void Process::Fail(void) { GCC_ASSERT(m_state == RUNNING || m_state == PAUSED); m_state = FAILED; } inline void Process::AttachChild(StrongProcessPtr pChild) { if (m_pChild) m_pChild->AttachChild(pChild); else m_pChild = pChild; } inline void Process::Pause(void) { if (m_state == RUNNING) m_state = PAUSED; else GCC_WARNING("Attempting to pause a process that isn't running"); } inline void Process::UnPause(void) { if (m_state == PAUSED) m_state = RUNNING; else GCC_WARNING("Attempting to unpause a process that isn't paused"); } /* inline StrongProcessPtr Process::GetTopLevelProcess(void) { if (m_pParent) return m_pParent->GetTopLevelProcess(); else return this; } */游戏Process包含游戏初始化后的各种状态与各种处理。
III. 游戏的Process管理类
#pragma once //======================================================================== // ProcessManager.h : defines common game events //======================================================================== #include "Process.h" #include <list> #include <iostream> using namespace std::list>; class ProcessManager { typedef std::list<StrongProcessPtr> ProcessList; ProcessList m_processList; public: // construction 明显的没有virtual 由于没有子类继承 ~ProcessManager(void); // interface unsigned int UpdateProcesses(unsigned long deltaMs); // updates all attached processes WeakProcessPtr AttachProcess(StrongProcessPtr pProcess); // attaches a process to the process mgr void AbortAllProcesses(bool immediate); // accessors unsigned int GetProcessCount(void) const { return m_processList.size(); } private: void ClearAllProcesses(void); // should only be called by the destructor };
//======================================================================== // ProcessManager.cpp : defines common game events //======================================================================== #include "GameCodeStd.h" #include "ProcessManager.h" //--------------------------------------------------------------------------------------------- // Destructor //--------------------------------------------------------------------------------------------- ProcessManager::~ProcessManager(void) { ClearAllProcesses(); } //--------------------------------------------------------------------------------------------- // The process update tick. Called every logic tick. This function returns the number of process chains that // succeeded in the upper 32 bits and the number of process chains that failed or were aborted in the lower 32 bits. //--------------------------------------------------------------------------------------------- unsigned int ProcessManager::UpdateProcesses(unsigned long deltaMs) { unsigned short int successCount = 0; unsigned short int failCount = 0; ProcessList::iterator it = m_processList.begin(); while (it != m_processList.end()) { // grab the next process StrongProcessPtr pCurrProcess = (*it); // save the iterator and increment the old one in case we need to remove this process from the list ProcessList::iterator thisIt = it; ++it; // process is uninitialized, so initialize it if (pCurrProcess->GetState() == Process::UNINITIALIZED) pCurrProcess->VOnInit(); // give the process an update tick if it's running if (pCurrProcess->GetState() == Process::RUNNING) pCurrProcess->VOnUpdate(deltaMs); // check to see if the process is dead if (pCurrProcess->IsDead()) { // run the appropriate exit function switch (pCurrProcess->GetState()) { case Process::SUCCEEDED : { pCurrProcess->VOnSuccess(); StrongProcessPtr pChild = pCurrProcess->RemoveChild(); if (pChild) AttachProcess(pChild); else ++successCount; // only counts if the whole chain completed break; } case Process::FAILED : { pCurrProcess->VOnFail(); ++failCount; break; } case Process::ABORTED : { pCurrProcess->VOnAbort(); ++failCount; break; } } // remove the process and destroy it m_processList.erase(thisIt); } } return ((successCount << 16) | failCount); } //--------------------------------------------------------------------------------------------- // Attaches the process to the process list so it can be run on the next update. //--------------------------------------------------------------------------------------------- WeakProcessPtr ProcessManager::AttachProcess(StrongProcessPtr pProcess) { m_processList.push_front(pProcess); return WeakProcessPtr(pProcess); } //--------------------------------------------------------------------------------------------- // Clears all processes (and DOESN'T run any exit code) //--------------------------------------------------------------------------------------------- void ProcessManager::ClearAllProcesses(void) { m_processList.clear(); } //--------------------------------------------------------------------------------------------- // Aborts all processes. If immediate == true, it immediately calls each ones OnAbort() function and destroys all // the processes. //--------------------------------------------------------------------------------------------- void ProcessManager::AbortAllProcesses(bool immediate) { ProcessList::iterator it = m_processList.begin(); while (it != m_processList.end()) { ProcessList::iterator tempIt = it; ++it; StrongProcessPtr pProcess = *tempIt; if (pProcess->IsAlive()) { pProcess->SetState(Process::ABORTED); if (immediate) { pProcess->VOnAbort(); m_processList.erase(tempIt); } } } }以上是进度管理的相关代码。包含不断的更新进度和杀死全部进度的控制。
以上的就是游戏的主循环的相关代码了。下一篇将是游戏音效管理相关的~~
- 本文已收录于下面专栏:
相关文章推荐
-
cocos2d-x游戏开发(四)游戏主循环
欢迎转载:http://blog.csdn.net/fylz1125/article/details/8518737 最终抽时间把这个游戏写完了。因为没有自拍神器。所以把它移植到了Andro...- fylz1125
- 2013-01-19 02:57
- 13506
-
Cocos2d-x 动手实现游戏主循环
因为Cocos2d-x封装的非常好。所以对于非常多新手,他们仅仅知道先new一个场景。在场景上加入布景或精灵,然后用Director的runWithScene便能够执行游戏了。假设给一个精灵加个动作。精灵就...- wxc237786026
- 2014-08-26 21:58
- 1504
-
cocos2d-x游戏循环与调度
每个游戏程序都有一个循环在不断执行,它是有导演对象来管理非常维护。假设须要场景中的精灵运动起来,我们能够在游戏循环中使用定时器(Scheduler)对精灵等对象的执行进行调度。由于Node类封装了Scheduler类。所以我们也可
- 关东升
- 2014-05-06 12:35
- 1259
-
server公共组件实现 -- mangos的游戏主循环
当阅读一项project的源代码时,我们大概会选择从main函数開始。而当開始一项新的project时,第一个写下的函数大多也是main。那我们就先来看看,游戏server代码实现中,main函数都做了些什么。
因为我在读技术...
- lfhfut
- 2007-09-18 00:05
- 4497
-
零基础学python-2.13 回到我们的游戏 增加循环
上次我们的游戏增加了条件推断。可是它仅仅可以给用户猜一次。很难猜中 所以,我们这节课在游戏里面增加循环,让用户多猜几次 先上原来的代码: print("---------欢迎来到猜数字的地方,请開始---------")#输出提示 guess=int(input("*数字区间0-100,请输入你猜的数字:"))#读取输入,然后赋值 print(guess)#打印输入 secret=18 if guess==secret: print("恭喜,猜对了") el- 李灵晖-raylee
- 2015-08-12 12:28
- 23
-
一款很不错的android游戏循环原理
讲解一款很不错的android游戏循环原理,給安卓游戏开发的朋友。[代码]DroidzActivity.java <
- jishublog
- 2013-06-28 11:01
- 369
-
Mangos源代码分析(7):server公共组件实现之游戏主循环
当阅读一项project的源代码时,我们大概会选择从main函数開始。而当開始一项新的project时,第一个写下的函数大多也是main。那我们就先来看看。游戏server代码实现中,main函数都做了些什么。 因为我...
- cgboss
- 2013-04-01 19:00
- 845
-
约瑟夫环,杀人游戏(静态循环链表实现)
认为用静态循环链表最划算了。 1、动态链表要动态分配,指针移来移去,释放指针等等,弄得非常烦。easy出错。 2、用循环链表是当然的了。 // DS: 似循环静态链表 #include <iostream> #include <cstdio> //#include <cstdlib> using namespace std; int Kill_You( const int sum = 1, const int distance = 1,- bcyy
- 2012-03-24 15:51
- 56
-
游戏主循环
摘自:http://my.oschina.net/u/243648/blog/71814 引言 游戏主循环是每一个游戏的心跳,输送着整个游戏须要的养分。不幸的是没有不论什么一篇好的文章来指导...- gg137608987
- 2012-10-10 15:58
- 1753
-
写一个好的游戏主循环
<p style="line-height: 25px; margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom:- v5browser
- 2013-04-28 00:31
- 256
0条评论