/*
概要(个人修改的内容已移除、修改字样标记)
*/
版本号:HGE_VERSION
为BorlandC编译提供数学函数支持(不需要,已移除)
基本数据类型:DWORD、WORD、BYTE
句柄类型:HTEXTURE、HTARGET、HEFFECT、HMUSIC、HSTREAM、HCHANNEL
导出dll的设置:EXPORT、CALL
数学常量:M_PI
32位色处理宏:ARGB、Set/GetA(R、G、B)
融合方式选项:Color/Alpha/ZWrite
Int设置:屏幕宽高、位深(bpp)、音效相关设置(采样频率、fx、music volume)、Fps
String设置:标题名、图标名、ini/log文件名
Bool设置:窗口/全屏,开启/关闭ZBuffer,开启/关闭TextureFilter、开启/关闭Sound、是否Suspend(挂起)、显示/隐藏鼠标、开启/关闭Splash(初始的Hge Logo动画)
Func设置:FrameFunc、RenderFunc、FocusLost/GainFunc、GFXRestoreFunc、ExitFunc
Hwnd状态:HWND(普通)、HWNDPARENT(父窗口)
电源状态控制(源码空定义,已移除)
Fps状态:是否开启垂直同步(可移动至Bool设置)
顶点格式:空间坐标(x,y)、depth(0..1)、color、纹理坐标(tx, ty)
图元类型:线段、三角形、四边形(顶点数组、纹理贴图句柄、blend方式)
Input事件:事件类型(KeyDown/Up, MButtonDown/Up, MouseMove/Wheel)、Key码,Flags(Shift, Ctrl, Alt, Capslock, ScrollLock, NumLock, Repeat),键盘按键(ascii码)/鼠标滚轮/鼠标位置(x, y)
外部接口--纯虚类Hge
hgeCreate全局C接口声明
虚拟键值(Virtual Key Cod
通过全局的C函数hgeCreate(C++因为提供函数重载,生成的接口函数名会带有@数字/字符(详见名称重整技术,hge.def文件也是用于解决这个问题的))获取Hge接口,__stdcall作用为让函数调用方负责清栈,在使用完Hge接口后调用Release来释放Hge接口(使用了引用计数技术,Release开销很小)。
hgeCreate实际工作是new一个Hge_Impl类(public继承于Hge类),并向上转型为Hge*返回。
获得Hge接口后自行设置一系列Bool、Int、Func、String状态/开关,然后再使用System_Initiate初始化,初始化过程中Hge会获取系统当前时间、Hge版本、操作系统版本、物理/虚拟内存情况,写入到log文件中,再注册窗口类、创建窗口、初始化Timer、Random、Input、Graphics(GFX)、Sound这些子系统,最后根据是否定义DEMO宏来播放Splash动画。
Hge_Impl下除了System之外,有Resource、Ini、Random、Timer、Sound(Effect, Music, Stream, Channel)、Input、Graphics(GFX)子系统。值得一提的是,子系统的所有成员变量以及方法全部都放在Hge_Impl类中,以良好的函数命名以及注释来划分。
System_Initiate结束之后使用System_Start来执行消息循环。
在收到结束消息,退出循环后使用System_Shutdown关闭各个子系统,最后再使用Release释放Hge接口。
值得一提的是随机数种子定义的是全局变量,这一点我觉得不如封装成Singleton类,种子为静态变量。
/*
Random概要
*/
unsigned int g_seed=0;
// 随机数种子的获取
// 初始化时Random_Seed(0);
void CALL HGE_Impl::Random_Seed(int seed)
{
if(!seed) g_seed=timeGetTime();
else g_seed=seed;
}
// 获取随机的int数
int CALL HGE_Impl::Random_Int(int min, int max)
{
g_seed=214013*g_seed+2531011;
return min+(g_seed ^ g_seed>>15)%(max-min+1);
}
// 获取随机的float数
float CALL HGE_Impl::Random_Float(float min, float max)
{
g_seed=214013*g_seed+2531011;
//return min+g_seed*(1.0f/4294967295.0f)*(max-min);
return min+(g_seed>>16)*(1.0f/65535.0f)*(max-min);
}
实现方式是将FrameFunc先设置为Demo.cpp中定义的DFrame(RenderFunc设为0,因为DFrame将Render的工作也写在了一起),贴一张Hge Logo图(数据在Demo.cpp的hgelogo数组中)到屏幕中心并根据时间修改其alpha值。
/*
splash screen animation
*/
dtime+=pHGE->Timer_GetDelta();
if(dtime<0.25)
alpha=(BYTE)((dtime*4)*0xFF);
else if(dtime<1.0)
alpha=0xFF;
else if(dtime<1.25)
alpha=(BYTE)((1.0f-(dtime-1.0f)*4)*0xFF);
// 计时器精度由初始化时timeBeginPeriod(1)设置为1ms
// 其他数值初始化如下
// fTime=0.0f;
// t0=t0fps=timeGetTime();
// dt=cfps=0;
// nFPS=0;
float fTime; // 当前时间,可由Timer_GetTime获取
float fDeltaTime; // 帧之间的时间间隔,可由Timer_GetDelta获取
DWORD nFixedDelta;
int nFPS; // Fps值,可由Timer_GetFPS获取
DWORD t0, t0fps, dt;
int cfps;
log文件写入是通过va_list可变长参数和vfprintf实现的:
void CALL HGE_Impl::System_Log(const char *szFormat, ...)
{
FILE *hf = NULL;
va_list ap;
if(!szLogFile[0]) return;
hf = fopen(szLogFile, "a");
if(!hf) return;
va_start(ap, szFormat);
vfprintf(hf, szFormat, ap);
va_end(ap);
fprintf(hf, "\n");
fclose(hf);
}
Ini文件用于保存本次程序运行的设置参数,使得在下一次程序执行时可直接读取这些参数。
Ini.cpp提供了对int、float、string三种类型参数的写入/读取支持,利用的是Get/WritePrivateProfileString函数。
实际上我觉得可以用lua这样的脚本语言来做配置参数表,自己封装Lua与C/C++交互的工具类,开源的库也有LuaBind。
// 音频的数据流使用单链表
struct CStreamList
{
HSTREAM hstream;
void* data;
CStreamList* next;
};
Sound系统封装了Bass库,支持了采样频率、多声道等高级效果。
struct CResourceList
{
char filename[_MAX_PATH];
char password[64];
CResourceList* next;
};
Resource系统封装了Zlib库,支持解压打包好的加密/不加密资源包,使用简单的单链表一一读取需要的资源。
struct CInputEventList
{
hgeInputEvent event;
CInputEventList* next;
};
鼠标/键盘输入的事件由单链表实现的队列管理。
Gfx封装了DirectX8的API。
struct CTextureList
{
HTEXTURE tex;
int width;
int height;
CTextureList* next;
};
struct CRenderTargetList
{
int width;
int height;
IDirect3DTexture8* pTex;
IDirect3DSurface8* pDepth;
CRenderTargetList* next;
};