绚丽的大型游戏背后,都存在着一个主循环,能依据游戏的进度控制游戏的状态。主循环管理游戏的正常执行。管理着游戏执行失败该怎样处理,对某些不能执行的情况给予杀死等等。
另外。在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
-
原创
- 27
-
粉丝
- 0
-
喜欢
- 0
0条评论