发布日期:2023-03-31来源:武汉北大青鸟武汉校区作者:武汉北大青鸟
网络游戏基础编程-零基础入门学习篇
网络游戏大家并不陌生,但游戏制作相信还是次吧,武汉北大青鸟光谷校区今天与大家一起学习的将是网络游戏基础入门编程,零基础也能学会的知识,不可错过哦。
本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。
1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。
本单例使用模板实现,代码如下:
[cpp] view plaincopyprint?//singleton.h #ifndef _SINGLETON_H #define _SINGLETON_H namespace Blaze { template<class T> class Singleton { public: static T* instance() { if (!_instance) { _instance = new T; } return _instance; } protected: /// 使用保护构造是为了用户不能在栈上声明一个实例 Singleton() {} private: static T* _instance; /// 实例静态指针 }; /// 静态实例指针初始化 template <class T> T* Singleton<T>::_instance = NULL; } // namespace #endif //_SINGLETON_H //singleton.h#ifndef _SINGLETON_H#define _SINGLETON_H namespace Blaze{template<class T>class Singleton{public:static T* instance(){if (!_instance){_instance = new T;}return _instance;} protected:/// 使用保护构造是为了用户不能在栈上声明一个实例Singleton() {} private:static T* _instance; /// 实例静态指针}; /// 静态实例指针初始化template <class T> T* Singleton<T>::_instance = NULL; } // namespace #endif //_SINGLETON_H
[cpp] view plaincopyprint?
//使用的时候只需要单件类继承此模板即可。
class Test : public Singleton
//使用的时候只需要单件类继承此模板即可。
class Test : public Singleton
2、dll(so)动态加载
在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。
实现使用模板,非常简单,代码如下
[cpp] view plaincopyprint?// lib_def.h #ifndef __LIB_DEF_H_ #define __LIB_DEF_H_ namespace Blaze { // DLL对象创建辅助类 template<const TCHAR*& szFileName> class DllApi { public: static BOOL Load() { if(!m_h) m_h = ::LoadLibrary(szFileName); return m_h != NULL; } static void Unload() { if(m_h) ::FreeLibrary(m_h); m_h = NULL; } protected: static HMODULE m_h; }; template<const TCHAR*& szFileName> HMODULE DllApi<szFileName>::m_h; //库文件前后缀 #ifdef WIN32 #define __DLL_PREFIX _T("") #define __DLL_SUFFIX _T(".dll") #else #define __DLL_PREFIX _T("lib") #define __DLL_SUFFIX _T(".so") #endif // 声明DLL文件名常量 #define DECLARE_DLL_FILE(module) extern "C" const TCHAR* module; // 定义DLL文件名常量 #if !defined(_LIB) && !defined(_USE_STATIC_LIB) #define DEFINE_DLL_FILE(module) extern "C" const TCHAR* module = _T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX; #else #define DEFINE_DLL_FILE(module) #endif } #endif 本例中使用了LoadLibrary,是windows的实现方法,在后面平台相关处理中,我会将linux的函数封装一下,和windows同名。此模板使用方法很简单: [cpp] view plaincopyprint?#if defined(_LIB) || defined(_USE_STATIC_LIB) // 静态库版本 #ifndef _LUA_ENGINE_API #define _LUA_ENGINE_API IMPORT_API #pragma comment(lib, MAKE_LIB_NAME(LuaEngine)) #endif _LUA_ENGINE_API ILuaEngine* GlobalLuaEngine(); #else DECLARE_DLL_FILE(LuaEngine); class GlobalLuaEngine : public DllApi<LuaEngine> { typedef ILuaEngine* (*CREATE_PROC)(); ILuaEngine* m_p; public: GlobalLuaEngine() : m_p(NULL) { Load(); static CREATE_PROC func; if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine"); if(func != NULL) m_p = func(); } operator ILuaEngine* (){ return m_p; } ILuaEngine* operator ->(){ return m_p; } }; #endif #if defined(_LIB) || defined(_USE_STATIC_LIB) // 静态库版本#ifndef _LUA_ENGINE_API#define _LUA_ENGINE_API IMPORT_API#pragma comment(lib, MAKE_LIB_NAME(LuaEngine))#endif _LUA_ENGINE_API ILuaEngine* GlobalLuaEngine();#elseDECLARE_DLL_FILE(LuaEngine);class GlobalLuaEngine : public DllApi<LuaEngine>{typedef ILuaEngine* (*CREATE_PROC)();ILuaEngine* m_p;public:GlobalLuaEngine() : m_p(NULL){Load();static CREATE_PROC func;if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine");if(func != NULL) m_p = func();}operator ILuaEngine* (){ return m_p; }ILuaEngine* operator ->(){ return m_p; }};
#endif
如上面代码所示,LuaEngine是一个dll,我们在加载它的时候,使用了一个额外的类,在他的构造函数里面加载了共享库。而且在应用级上也与平台无关。
3、跨平台的若干处理
windows的处理相当简单,只是定义一些简单的宏。
[cpp] view plaincopyprint?// gwindef.h : windows开发定义文件 #ifndef __G_WIN_DEF_H_ #define __G_WIN_DEF_H_ #include <windows.h> #include <process.h> #include <tchar.h> #include <unknwn.h> #include <stdio.h> #include <stdlib.h> #define SYS_API WINAPI #define STD_CALL __stdcall #if !defined(_LIB) #define EXPORT_API extern "C" _declspec(dllexport) #else #define EXPORT_API extern "C" #endif #if !defined(_LIB) && !defined(_USE_STATIC_LIB) #define IMPORT_API extern "C" _declspec(dllimport) #else #define IMPORT_API extern "C" #endif #endif // ndef __G_WIN_DEF_H_ 而为了开发的时候去除平台无关性,在linux的开发中,我们需要做一些包装,使其在开发过程中和window代码一致。[cpp] view plaincopyprint?// glindef.h : linux开发定义文件 #ifndef __G_LIN_DEF_H_ #define __G_LIN_DEF_H_ // #include <stdlib.h> #include <stdio.h> #include <string.h> #include <wchar.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <errno.h> #include <sys/times.h> #include <time.h> #include <dlfcn.h> #include <sys/types.h> #include <linux/unistd.h> inline _syscall0(pid_t, gettid) /* Using syscall(2) may be preferable; see intro(2) */ #ifdef UNICODE #define _T(str) L##str #else #define _T(str) str #endif #define TRUE 1 #define FALSE 0 #define MAX_PATH 256 #define SYS_API #define STD_CALL #define EXPORT_API extern "C" #define IMPORT_API extern "C" /// HRESULT 常量定义 typedef long HRESULT; enum HResult { S_OK = ((HRESULT)0x00000000), /**< 成功,值为0 */ S_FALSE = ((HRESULT)0x00000001), /**< 成功,但值为1 */ E_FAIL = _HRESULT_TYPEDEF_(0x80004005), /**< 未定义错误 */ E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001), /**< 接口未实现 */ E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E), /**< 内存不足 */ E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057), /**< 无效参数 */ E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002), /**< 接口不存在 */ E_POINTER = _HRESULT_TYPEDEF_(0x80004003), /**< 无效指针 */ E_HANDLE = _HRESULT_TYPEDEF_(0x80070006), /**< 无效句柄 */ E_ABORT = _HRESULT_TYPEDEF_(0x80004004), /**< 操作被取消 */ E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005), /**< 访问拒绝 */ E_PENDING = _HRESULT_TYPEDEF_(0x8000000A), /**< 操作被挂起 */ E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF) /**< 未预料的错误 */ }; /// 判定 HRESULT 值是否为成功值 #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) /// 判定 HRESULT 值是否为失败值 #define FAILED(Status) ((HRESULT)(Status) < 0) /// GUID 类型定义 /** 要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成, 以确保其性。 接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/ struct GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8]; }; typedef GUID IID; typedef GUID CLSID; #define REFGUID const GUID& #define REFIID const IID& #define REFCLSID const CLSID& /// 判断两个 GUID 是否相等(内联版) inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] && ((long*)&rguid1)[1] == ((long*)&rguid2)[1] && ((long*)&rguid1)[2] == ((long*)&rguid2)[2] && ((long*)&rguid1)[3] == ((long*)&rguid2)[3]; } /// 判断两个 GUID 是否相等 inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return !memcmp(&rguid1, &rguid2, sizeof(GUID)); } #define CopyMemory(dest, src, len) memcpy((dest), (src),(len)) #define ZeroMemory(dest, len) memset((dest), 0, (len)) #define FillMemory(dest, len, value) memset((dest), value, (len)) #define GetCurrentThreadId gettid #define OutputDebugString(str) tprintf(_T("%s"), str) #define LoadLibrary(file) dlopen(file, RTLD_NOW) #define FreeLibrary dlclose #define GetProcAddress dlsym inline int GetLastError() { return errno; } inline DWORD GetTickCount() { static int clkTck = 0; if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK); return (DWORD)::times(NULL) * clkTck; // 不能溢出 } inline void Sleep(DWORD ms) { struct timespec req, rem; req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000; while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem; } inline long InterlockedIncrement(long volatile* v) { long src = 1; /* Modern 486+ processor */ __asm__ __volatile__( "lock xaddl %0, %1;" :"=r"(src), "=m"(*v) :"0"(src)); return src + 1; } inline long InterlockedDecrement(long volatile* v) { long src = -1; /* Modern 486+ processor */ __asm__ __volatile__( "lock xaddl %0, %1;" :"=r"(src), "=m"(*v) :"0"(src)); return src - 1; } #define stricmp strcasecmp #include <ctype.h> inline void strupr(char *s) { while (*s) { *s = toupper((unsigned char) *s); s++; } } inline void strlwr(char *s) { while (*s) { *s = tolower((unsigned char) *s); s++; } } #endif // ndef __G_LIN_DEF_H_ // glindef.h : linux开发定义文件#ifndef __G_LIN_DEF_H_#define __G_LIN_DEF_H_// #include <stdlib.h>#include <stdio.h>#include <string.h>#include <wchar.h>#include <unistd.h>#include <pthread.h>#include <semaphore.h>#include <errno.h>#include <sys/times.h>#include <time.h>#include <dlfcn.h>#include <sys/types.h>#include <linux/unistd.h> inline _syscall0(pid_t, gettid) /* Using syscall(2) may be preferable; see intro(2) */ #ifdef UNICODE#define _T(str) L##str#else#define _T(str) str#endif #define TRUE 1#define FALSE 0 #define MAX_PATH 256 #define SYS_API#define STD_CALL#define EXPORT_API extern "C"#define IMPORT_API extern "C" /// HRESULT 常量定义typedef long HRESULT;enum HResult{S_OK = ((HRESULT)0x00000000), /**< 成功,值为0 */S_FALSE = ((HRESULT)0x00000001), /**< 成功,但值为1 */E_FAIL = _HRESULT_TYPEDEF_(0x80004005), /**< 未定义错误 */E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001), /**< 接口未实现 */E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E), /**< 内存不足 */E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057), /**< 无效参数 */E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002), /**< 接口不存在 */E_POINTER = _HRESULT_TYPEDEF_(0x80004003), /**< 无效指针 */E_HANDLE = _HRESULT_TYPEDEF_(0x80070006), /**< 无效句柄 */E_ABORT = _HRESULT_TYPEDEF_(0x80004004), /**< 操作被取消 */E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005), /**< 访问拒绝 */E_PENDING = _HRESULT_TYPEDEF_(0x8000000A), /**< 操作被挂起 */E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF) /**< 未预料的错误 */}; /// 判定 HRESULT 值是否为成功值#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)/// 判定 HRESULT 值是否为失败值#define FAILED(Status) ((HRESULT)(Status) < 0) /// GUID 类型定义/**要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成,以确保其性。接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/struct GUID{ unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8];}; typedef GUID IID;typedef GUID CLSID;#define REFGUID const GUID&#define REFIID const IID&#define REFCLSID const CLSID& /// 判断两个 GUID 是否相等(内联版)inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2){return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] && ((long*)&rguid1)[1] == ((long*)&rguid2)[1] &&((long*)&rguid1)[2] == ((long*)&rguid2)[2] &&((long*)&rguid1)[3] == ((long*)&rguid2)[3];} /// 判断两个 GUID 是否相等inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2){ return !memcmp(&rguid1, &rguid2, sizeof(GUID));} #define CopyMemory(dest, src, len) memcpy((dest), (src),(len))#define ZeroMemory(dest, len) memset((dest), 0, (len))#define FillMemory(dest, len, value) memset((dest), value, (len))#define GetCurrentThreadId gettid#define OutputDebugString(str) tprintf(_T("%s"), str)#define LoadLibrary(file) dlopen(file, RTLD_NOW)#define FreeLibrary dlclose#define GetProcAddress dlsym inline int GetLastError(){return errno;} inline DWORD GetTickCount(){static int clkTck = 0;if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK);return (DWORD)::times(NULL) * clkTck; // 不能溢出} inline void Sleep(DWORD ms){struct timespec req, rem;req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000;while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem;} inline long InterlockedIncrement(long volatile* v){long src = 1;/* Modern 486+ processor */__asm__ __volatile__("lock xaddl %0, %1;":"=r"(src), "=m"(*v):"0"(src));return src + 1;} inline long InterlockedDecrement(long volatile* v){long src = -1;/* Modern 486+ processor */__asm__ __volatile__("lock xaddl %0, %1;":"=r"(src), "=m"(*v):"0"(src));return src - 1;} #define stricmp strcasecmp #include <ctype.h>inline void strupr(char *s){while (*s) {*s = toupper((unsigned char) *s);s++;}} inline void strlwr(char *s){while (*s) {*s = tolower((unsigned char) *s);s++;}} #endif // ndef __G_LIN_DEF_H_
代码都比较简单,我也不对这些做详细的解析,功能就是对一些常用函数改装成windows相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。
对于还未能了解的同学,可以参加武汉北大青鸟光谷校区讲师们为大家安排的小游戏制作免费公开课,贪吃蛇游戏、会说话的机器人、QQ游戏等等都能够亲自去实现它们。
Copyright (c) 2006-2023 武汉宏鹏教育咨询有限公司 版权所有 All Rights Reserved.