七 26
邱金武game
因工作关系,花了几天时间学习了下Lua,对其基本语法、和C/C++的交互有一个初步的了解。
因之前有看过Python,所以熟悉Lua没有花多少精力,可以去官网看手册,网上也可以找到大把免费电子书,包括中文的。去官网下载的最新安装包,安装后发现有一个工具软件”SciTE”挺好用,至少在学习阶段跑跑测试用例足够了。如果喜欢用VS编辑,可以考虑安装一个VS插件(Google了一个http://vslua.codeplex.com/ 还有其他的),虽然可以通过若干种方法使用VS编译Lua文件(1.自VS2005开始被支持的自定义编译规则、2.“工具”->“外部工具”),不过没发现一个好的调试办法,也无法运行之,所以放弃了。尝试使用Eclipse,不过杯具的是那个插件(http://luaeclipse.luaforge.net/ )居然无法安装!
和很多人一样,我学习Lua之意并不在于其本身,更多的是它的C++扩展。包括在C++中使用Lua脚本和使用C++扩展Lua库。这在游戏领域和UI领域用得特别广泛。著名的跨平台UI库WxWidget就有Lua扩展WxLua(http://wxlua.sourceforge.net/ )。游戏UI库CEGUI也默认支持Lua。
在C++中调用Lua脚本的应用相对少见,更多的时候是使用C++来扩展Lua库。对于后者有不少成熟的封装库可以使用,例如tolua++、luabind、cpplua等,而在C++中调用Lua一般直接使用lua C API来实现。
用C++扩展Lua库有两种情况,一种是提供封装库文件(一般是.dll或.so文件),在这种情况下,Lua程序员可以用Lua完成全部功能,前面提到的WxLua(WxWidget貌似被很多语言河蟹了一遍,包括WxPython、WxLua、wxRuby 、wxErlang 、wxPerl )就是这种情况。
有时候在一个特定项目中,我们只需要将部分功能的实现从C++转移到Lua。这在游戏开发中特别常见,这样可以让一些非程序开发人员(例如策划、美工)来完成最繁琐的业务逻辑开发工作。此时一般不提供独立的Lua库文件,而是在一个C++静态库或者C++程序内部实现之。在C++程序调用基于该库的Lua脚本前先动态加载该库。这种处理方法实际上用到了两个方向的Lua C API。这样处理有更好的安全性,因为暴露给Lua的接口必须通过一个特定程序才能加载。
七 17
邱金武game
1.窗口消息
尽管HGE的UI框架(下面以HGEUI称呼之)和当前主流的UI框架比起来还很简陋,不过麻雀虽小,五脏俱全,所以打算以一个通用UI框架的视角来观察之。
研究一个UI框架,首先得从最重要的窗口消息处理入手。HGEUI的窗口消息和HGE的消息混在一起,所以就先看看HGE消息入手。
1.1.HGE窗口消息
HGE通过Input接口来提供消息的访问,这种方式不是Windows默认的触发性消息,而是轮询方式。程序如果需要用到窗口消息,自己去查询。具体的API见【hge.h/input.cpp】的下列成员函数:
virtual void CALL Input_GetMousePos(float *x, float *y) = 0;
virtual void CALL Input_SetMousePos(float x, float y) = 0;
virtual int CALL Input_GetMouseWheel() = 0;
virtual bool CALL Input_IsMouseOver() = 0;
virtual bool CALL Input_KeyDown(int key) = 0;
virtual bool CALL Input_KeyUp(int key) = 0;
virtual bool CALL Input_GetKeyState(int key) = 0;
virtual char* CALL Input_GetKeyName(int key) = 0;
virtual int CALL Input_GetKey() = 0;
virtual int CALL Input_GetChar() = 0;
virtual bool CALL Input_GetEvent(hgeInputEvent *event) = 0;
这些函数都是查询函数,查询数据来自该类的成员变量,这些变量会随着Windows窗口消息的触发而动态更新。这些更新操作通过几个函数实现。
-
_UpdateMouse:该函数在HGE主循环中被调用,主要更新鼠标是否在当前窗口上
-
_BuildEvent :该函数在Windows窗口回调中被调用,更新其他窗口消息相关的变量。见system.cpp中的WindowProc函数。
1.2.HGEUI窗口消息
HGEUI继承了它老爹的优良传统,同样不使用触发式消息系统,而是让底层调用它的相关函数来更新自己的状态和完成绘制。在这些函数里面通过上述的函数获得窗口消息,然后更新自己。
2.Ui绘制
HGEUI中的绘制很简单,就是通过HGE对DX封装的那一套东西画画图。
3.光标
如果仔细观察可能会发现,HGE默认没有光标,不过可以通过设置HGE_HIDEMOUSE标志来激活光标。不过鉴于游戏一般都使用自定义光标,所以激活这个选项意义不大。
HGE中的自定义光标在hgeGUI中被支持。可以通过hgeGUI类的SetCursor函数设置光标图片。
4.焦点
如果仔细看源码,会发现HGE有焦点的代码,而GUI这边又有焦点相关的代码,实际上这两者不一样,前者关于窗口焦点,后者关于控件焦点。如果不懂相关概念,见这里【http://www.ibm.com/developerworks/cn/linux/l-cn-focus/index.html 】。
作为一条不成文的规矩,控件焦点可以通过Tab键导航,不过貌似HGEUI并没有遵守这个规矩。不过它提供其他的焦点导航方式:
-
HGEGUI_NONAVKEYS :无键盘导航
-
HGEGUI_LEFTRIGHT :左右按键导航
-
HGEGUI_UPDOWN :上下按键导航
-
HGEGUI_CYCLED :循环
焦点链【保存当前控件上/下一个焦点控件的数据结构】通过hgeGUIObject类的next和prev指针实现。这两个指针在hgeGUI类的AddCtrl函数中被初始化。所以HGEUI的焦点链和AddCtrl的顺序是一致的。可以通过hgeGUI类的SetFocus来强制特定控件获得焦点。当前获得焦点的控件存储在hgeGUI类中的ctrlFocus指针中。
除了获得焦点的控件外,还有一个特别的控件,即鼠标所在的窗口(Hover)。一般情况下,键盘消息经过HGE那一层处理和过滤后,会直接送到获得焦点的控件,不过鼠标消息则不定,鼠标按下消息就会送到鼠标所在的窗口,例如单击事件。这个控件存储在hgeGUI类中的ctrlOver指针中。
HGEUI通过比较当前鼠标坐标和控件区域来更新这个指针。
5.控件
千万不要跟我说找不到HGEUI控件的Windows窗口句柄。
HGEUI的控件必须继承自hgeGUIObject,该函数包含一个纯虚函数Render,所以必须实现之,不过还有一个重要的函数有必要实现,Update函数。这两个函数一起完成控件的绘制,Render函数完成具体的绘制操作(渲染),可以认为是普通UI模型中的Paint函数,而Update做一些渲染前的准备,可以认为是普通UI模型中的Layout函数。个人觉得这两个函数和HGE模型中的两个系统回调类似,RenderFunc 回调匹配Render函数,FrameFunc回调匹配Update函数。
5.1.控件初始化和终止
在HGEUI中包含一个普通UI框架少见的feature,即Enter和Leave。即控件初始化和终止时的特殊处理支持。这样当控件第一次被绘制和最后退出时可以有特殊的表现,例如开始动画和终止动画。这个操作通过hgeGUIObject类的Enter和Leave函数来实现。
那HGE怎么知道控件是否初始化/终止完了呢?通过hgeGUIObject类的IsDone函数返回值,如果返回true则表示已经结束。该函数默认总是返回true,表示随时可以被河蟹。
5.2.控件消息
控件除了将UI渲染分离成多个子模块实现外,最重要的功能就是用户交互。那HGEUI怎么接收和处理窗口消息呢?继承hgeGUIObject类的下列虚函数:
virtual bool MouseMove(float x, float y) { return false; }
virtual bool MouseLButton(bool bDown) { return false; }
virtual bool MouseRButton(bool bDown) { return false; }
virtual bool MouseWheel(int nNotches) { return false; }
virtual bool KeyClick(int key, int chr) { return false; }
此外处理键盘鼠标消息外,HGEUI控件还可以响应Focus事件,见hgeGUIObject类的虚函数:
virtual void Focus(bool bFocused) {}
5.3.消息回调
控件响应消息除了更新自己外,更多的是做逻辑处理。最简单的方法就是将逻辑处理代码写到窗口消息回调函数里面,不过这样有一个致命的缺陷,控件毫无移植性。一般的做法是控件提供接口来注册回调,当收到相关窗口消息后调用该回调。或者将所有控件的逻辑处理重定向到某一个位置,通过一个ID(一般用控件ID)来区分不同的控件源。
HGE使用后者,不过仅仅对鼠标消息有效,而且实现很诡异。见hgeGUI的ProcessCtrl函数。如果该类的bLPressed属性被设置,它会返回该控件的ID。可以参考官方源码sample6。
在这里【http://blog.csdn.net/ShowLong/archive/2007/07/02/1675197.aspx】有一个关于HGE中文输入控件的实现,可以借鉴一下。
七 14
邱金武game
1.精灵
精灵(hgeSprite)是HgeHelper中实现的一个用得非常多的类,很多更高级的模块也基于该类,包括字体,所以在详细阐述字体之前,先说说它。
hgeSprite对纹理渲染进行简单的封装,它通过一个纹理句柄和区域参数进行初始化,不过底层的渲染参数仍旧基于hgeQuad,见hgeSprite 下列成员函数:
void Render(float x, float y);
void RenderEx(float x, float y, float rot,
float hscale=1.0f, float vscale=0.0f);
void RenderStretch(float x1, float y1, float x2, float y2);
void Render4V(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
hgeSprite提供了更为丰富的属性Get/Set,以及更为高级的渲染方式。包括对纹理按比率放缩,旋转,任意矩形放缩和任意四边形放缩。
2.字体
字体(hgeFont)实现基于 hgeSprite,和普通的UI字体不同,HGE中字体实际上是贴图,而这些图片这通过HGE自定义的字体文件提供,后缀为fnt,可以通过HGE官方提供的工具生成。
2.1.字体使用
HGE字体使用相对简单,不过和其他很多游戏库一样,中文支持是个麻烦。默认的用法如下:
//创建字体对象
HgeFont* fnt=new hgeFont("font1.fnt");
//绘制字体
fnt->printf(5, 5, HGETEXT_LEFT,
"dt:%.3f\nFPS:%d (constant)",
hge->Timer_GetDelta(),
hge->Timer_GetFPS());
//销毁字体对象
delete fnt
当然在printf之前,也可以设置很多常用的字体参数,如颜色、大小、字距、alpha等。不过不支持字体,所以如果希望使用多个字体,可以定义多个字体文件,并且分配多个 hgeFont实例。详细的字体参数配置见 hgeFont类。
2.2.字体定义
HGE一个字体包含两部分,一个字体配置文件(.fnt),另一个是字体字模文件(.png)。之所以使用png可能是该格式支持alpha通道吧。
fnt文件定义了匹配的字模图片文件,以及每一个字符所占用的区域。一个典型的英文字体配置如下:
|
[HGEFONT]
Bitmap=font1.png
Char=” “,0,0,8,27,0,0
Char=”!”,8,0,5,27,0,0
Char=”"”,13,0,9,27,0,0
Char=”#”,22,0,13,27,0,0
|
很显然这种“字体”和点阵字体差不多,所以如果放缩太多容易造成马赛克或者走样。
2.3.字体渲染
前面提到, hgeFont基于 hgeSprite。实际上,渲染字体就是根据字符序列获得匹配的字模,然后做变换(放缩、旋转等)、Layout,最后通过 hgeSprite来渲染。
hgeFont使用成员 hTexture存储纹理句柄,而成员letters[256]则存储“256”个字符。这些成员在构造函数中被初始化。具体的渲染操作在函数hgeFont::Render中实现,在那里还包含简单的Layout。
2.4.中文支持
现在网上有很多牛人提供了中文支持解决方案。说起来中文支持原理很简单,不过很多细节很难把握。
因为HGE字体渲染使用“点阵字模”,所以自动生成一张包含中文的字模就可以了。不过有几个问题:
-
汉字不和ascii码一样就那么几个(我是不知道确切有多少个汉字的),所以如果把所以汉字都贴出来会不会很大,效率会不会受影响,一张图片能不能容得下。而如果只保留常见字,那这个“度”怎么把握?
-
ascii的索引非常方便,而且效率非常高,可是中文首先字符编码有多种,有些字符编码还是变长,所以对字符作一个好的索引比较麻烦。特别是只取部分汉字时。
-
正因为汉字特别多,所以必须有一个自动化的工具生成字模图。
当然HGE默认的字体渲染框架对于中文并不一定是最合适的,我们完全可以对其源码做适当的修改(甚至重写)已获得更高的性能。
七 14
邱金武game
1.Sample
1.1.Hello world
具体可以参考HGE官方源码包中的sample1【hge_tut01】
#include "hge.h"
HGE *hge = 0;
bool FrameFunc()
{
if (hge->Input_GetKeyState(HGEK_ESCAPE)) return true;
return false;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
hge = hgeCreate(HGE_VERSION);
hge->System_SetState(HGE_FRAMEFUNC, FrameFunc);
hge->System_SetState(HGE_TITLE, "HGE Tutorial 01 -
Minimal HGE application");
hge->System_SetState(HGE_WINDOWED, true);
hge->System_SetState(HGE_USESOUND, false);
if(hge->System_Initiate())
{
hge->System_Start();
}
hge->System_Shutdown();
hge->Release();
return 0;
}
为了保持简单,去除了注释和错误处理
HGE程序要点:
-
创建HGE结构,并获得句柄(指针)
-
设置该句柄的相关参数,其中重要的参数包括
-
HGE_FRAMEFUNC :设置帧回调函数
-
HGE_RENDERFUNC :设置渲染回调函数
-
HGE_SCREENWIDTH :设置窗口宽
-
HGE_SCREENHEIGHT :设置窗口高
-
HGE_WINDOWED :设置窗口模式(是否全屏)
-
HGE_TITLE :设置窗口标题
-
HGE_FPS :设置fps上线
-
HGE_LOGFILE :设置日志文件名称
-
初始化HGE结构
-
加载其他资源和其他初始化工作(上面的程序没有)
-
启动主循环,正式进入运行状态
-
释放、回收资源
上面的程序很简单,仅仅启动一个窗口(屏幕全黑),并且在玩家按下Esc键后退出。
1.2.帧回调函数
帧回调函数(FrameFunc)执行一些渲染初始化的工作,特别是Layout,消息也在这里被处理。该函数返回true将导致消息循环退出。
一般情况下,该函数内的处理流程是:
-
获取、分析窗口(和控件)消息
-
判断是否退出
-
根据窗口消息来决定渲染的行为(例如渲染的位置、控件的状态等)
1.3.渲染回调函数
渲染回调函数(RenderFunc)执行具体的渲染工作。如果无需渲染,可不需要,如上面的程序。
2.HGE总体架构
HGE包含三个大的模块,包括两个程序模块和一个工具模块。两个程序模块分别以程序工程的形式提供。所以如果需要使用HGE,需要连接上述两个工程产生的库文件。分别是HgeCore和HgeHelper。
HgeCore是整个HGE引擎的基础,它是HgeHelper的基础,主要实现一个基本功能的封装。HgeHelper基于HgeCore开发,实现了一些更为复杂的操作,方便游戏开发者。这个模块是游戏开发者最常打交道的部分。不过如果觉得HgeHelper太烂或者无法满足需求,完全可以基于HgeCore自写一个HgeHelper。当然也可以自写HgeCore,不过这跟重新弄个引擎就没啥区别了。
工具模块主要包含一些资源生成相关的工具,例如字体、粒子系统配置的生成。
3.HgeCore
对Win Gui、Dx8以及相关第三方库的一个简单封装,具体内容:
-
图片纹理支持,包括bmp、jpg、png等主流格式支持,底层使用DX8。
-
音频支持(OSS库)
-
窗口消息支持(输入设备,鼠标、键盘)
-
窗口模式支持(全屏和窗口)
-
资源支持(Zip文件)
-
日志支持
-
配置文件支持(.ini)
3.1.程序结构
HGE将上述所有接口都定义在【include\hge.h】,在这里HGE定义一个虚基类。具体的实现定义在子类HGE_Impl中【core\hge_impl.h】。而该类的实现则分别在
-
graphics.cpp :纹理贴图支持
-
ini.cpp :ini文件支持
-
input.cpp :窗口消息支持
-
random.cpp :随机数支持
-
resource.cpp :资源支持(zip)
-
sound.cpp :音频支持
-
timer.cpp :时间相关支持
-
system.cpp :系统支持,包括初始化、终止、消息循环以及配置等。
4.基本操作
4.1.音效
HGE音效使用BASS库【http://www.un4seen.com/ 】,使用非常简单,见下列几个API:
//加载和释放音效
HEFFECT snd=hge->Effect_Load("menu.wav");
hge->Effect_Free(snd);
//播放
hge->Effect_Play(snd);
hge->Effect_PlayEx(snd,100,pan,pitch);
4.2.窗口消息
获得窗口消息很简单,见hge.h中的Input系列函数,例如判断是否按下Esc键可以使用如下代码:
if (hge->Input_GetKeyState(HGEK_ESCAPE))
HGE每一次刷新都会轮询当前的输入情况,具体见函数System.cpp中的窗口回调WindowProc以及HGE_Impl::System_Start()中的主循环实现。HGE会将上次的窗口消息保存起来,以供接下来的查询。
4.3.纹理渲染
HGE中可以渲染矩形和三角形(这里不阐述)。分别通过结构hgeQuad和hgeTriple来存储渲染信息。渲染底层实现使用DX8,理论上可以移植到DX后续版本或者OpenGL。
HgeQuad贴图包含三个部分:
-
纹理句柄,纹理可以从主流的图片格式中获取,例如jpg、png、bmp等。
-
渲染位置,由四个点组成,顺序依次是左上、右上、右下、坐下。如果纹理大小和渲染位置不一致,纹理将被拉伸(或缩小)以切合渲染位置大小。大小使用像素作为单位。
-
纹理区域,同样包含四个点,不过和“渲染位置”不同的是它使用比率作为单位,例如【(0,0)、(1,0)、(1,1)、(0,1)】表示使用整个纹理区域。很显然,如果想使用左上角1/4区域,可以使用【(0,0)、(0.5,0)、(0.5,0.5)、(0,0.5)】。
关于纹理渲染,建议参考HGE官方sample2【hge_tut02】,如果看过官方sample6【hge_tut06】,可能会疑问,怎么实现平铺效果,背景图的动态平移又怎么实现呢?修改HgeQuad中的纹理区域可以做到。实际上,任何一张纹理在HgeQuad中就是一个由该纹理一张挨一张的平铺平面,所以我们只要稍微计算一下大小即可平铺。
例如纹理大小为(W,H),而渲染区域是(W1,H1)。我们将纹理区域设置为【(0,0)、(W1/W,0)、(W1/W,H1/H)、(0,H1/H)】即可。而如果需要实现动态平移,可以设置两个变量(X,Y),将【(X,Y)、(X+W1/W,Y)、(X+W1/W,Y+H1/H)、(X,Y+H1/H)】。其中X,Y分别表示平移的比率,例如需要向上平移,就可以将X根据时间不断增大(超过纹理大小置零)。
很显然,如果想使用句柄纹理来平铺,上述方法无法实现。
关于渲染众多参数的说明,可以参考下这里【http://blog.csdn.net/wenzhoufeng22/archive/2008/04/22/2316016.aspx 】
5.时间控制
时间控制主要包括三个方面:
-
运行总时间
-
上次刷新间隔
-
fps控制
时间控制相关的变量,见类HGE_Impl成员
float fTime;
float fDeltaTime;
DWORD nFixedDelta;
int nFPS;
DWORD t0, t0fps, dt;
int cfps;
int nHGEFPS;
-
nHGEFPS :设定的最高fps,默认为不设置,即0
-
nFixedDelta :两次刷新的最小时间间隔,默认为0,更新nHGEFPS时会同时更新它。
-
t0 :上次刷新的时间戳(ms)
-
t0fps :统计fps的初始时间戳,如果间隔超过1s,该值被复位
-
dt :本次刷新和上次刷新的间隔(ms)
-
fDeltaTime :本次刷新和上次刷新的间隔(s)
-
fTime :自启动后已经运行的时间(s)
-
cfps :统计fps的计数器,在1s内,每刷新一次该值加一
-
nFPS :fps,当统计fps间隔超过1s后,该值被复制为cfps
上述两个变量可通过下面三个成员获得:
virtual float CALL Timer_GetTime();
virtual float CALL Timer_GetDelta();
virtual int CALL Timer_GetFPS();
而设置FPS上线,则通过设置选项来完成:
hge->System_SetState(HGE_FPS, X);
5.1.时间计数
在循环启动和每一次刷新后,HGE会保存当前时间作为“上次运行时间”。等到下次刷新开始前会用当前时间减去“上次运行时间”来获得“上次刷新间隔”,同时一个会将该值累加到“运行总时间”变量中。
5.2.Fps计数
HGE保存一个计数器C1,初始值为循环启动时间。每次刷新,会用当前时间减去该值。如果小于1000(ms),则将一个临时FPS计数器C2加1(初始值为0)。否则将C1赋值为当前时间,并且将C2赋值到FPS正式计数器(可以通过 Timer_GetFPS函数访问)然后将C2清零。
5.3.Fps限制
HGE默认设置最高的FPS是1000,因为在HGE_Impl::System_Start() 中有如下显示:
do { dt=timeGetTime() - t0; } while(dt < 1);
两次刷新必须要大于1ms。不过程序员可以通过设置相关参数设置更低的Fps上限。当设置上限后,Hge会更新变量 nFixedDelta。然后在HGE_Impl::System_Start() 函数中判断是否低于这个时间阀值,如果低于这个阀值,就跳过刷新。
if(dt >= nFixedDelta){
//刷新操作
6.HGE logo
HGE提供的sample中启动时总是会出现一个启动图标,如下:

如果不希望显示,可以在初始化之前通过设置下面的标志位取消
HGE.System_SetState(HGE_SHOWSPLASH, false)
而如果希望从源码中去掉这些代码实现,可以Undef DEMO宏。具体的实现在【core/demo.cpp】。调用代码如下【HGE_Impl::System_SetStateInt函数】:
if(pHGE->bDMO)
{......
DInit();
pHGE->System_Start();
Ddone();
......
}
6.1.Demo渲染
具体的实现很简单,如下:
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);
else return true;
一共持续1.25s,在前0.25s将alpha通道从0递增到1,到1.0s时保持1不变,而到1.25s时又慢慢地从1递减到0。超过1.25s后返回true退出循环。从而实现一个logo慢慢显示而后又慢慢消失的效果。
几个不错的blog,排名不分先后
http://hi.baidu.com/%B3%FE%D3%CE%D6%D0%D0%C4/blog/category/hge%D1%A7%CF%B0/index/1
http://yoyo.is-programmer.com/categories/3035/posts?page=4
http://blog.csdn.net/wenzhoufeng22/archive/2008/04/22/2316002.aspx
http://blog.csdn.net/ShowLong/category/316668.aspx
七 11
邱金武game
经高人指点,选择开源2D游戏引擎HGE【http://hge.relishgames.com】作为涉足游戏开发的切入点。
第一件事自然是下载源码,然后在本地编译。见这里【http://hge.relishgames.com/files/hge17.zip 】。之所以选择1.7而不是选择最新的1.8是因为编译1.8在我本地会出错(貌似不止我),如下:
|
1> Creating library ..\..\lib\vc\hge.lib and object ..\..\lib\vc\hge.exp
1>system.obj : error LNK2019: unresolved external symbol "public: void __thiscall HGE_Impl::_InitPowerStatus(void)" (?_InitPowerStatus@HGE_Impl@@QAEXXZ) referenced in function "public: virtual bool __stdcall HGE_Impl::System_Initiate(void)" (?System_Initiate@HGE_Impl@@UAG_NXZ)
1>system.obj : error LNK2019: unresolved external symbol "public: void __thiscall HGE_Impl::_DonePowerStatus(void)" (?_DonePowerStatus@HGE_Impl@@QAEXXZ) referenced in function "public: virtual void __stdcall HGE_Impl::System_Shutdown(void)" (?System_Shutdown@HGE_Impl@@UAGXXZ)
1>system.obj : error LNK2019: unresolved external symbol "public: void __thiscall HGE_Impl::_UpdatePowerStatus(void)" (?_UpdatePowerStatus@HGE_Impl@@QAEXXZ) referenced in function "public: virtual bool __stdcall HGE_Impl::System_Start(void)" (?System_Start@HGE_Impl@@UAG_NXZ)
1>..\..\hge.dll : fatal error LNK1120: 3 unresolved externals
|
这里【http://www.hgechina.com/forum/viewtopic.php?start=0&p=191&topic_view=0&sid=271f494ec860de6ee5aafb5846ae4e0f 】有讲到编译时的一系列问题。
HGE官方版使用DX8,所以为了成功编译需要安装DX8或者下载其头文件和lib。我选择后者,下载见这里【http://resource.gameres.com/dx81.zip】,因为不想装太多东西,况且Win 7也不一定支持那么老掉牙的东东。
源码中需要编译的项目主要是【src/core】和【src/helpers】,不过编译前者又出错了,如下:
|
1>c:\program files\microsoft sdks\windows\v6.0a\include\winnt.h(236) : error C2146: syntax error : missing ‘;’ before identifier ‘PVOID64′
1>c:\program files\microsoft sdks\windows\v6.0a\include\winnt.h(236) : error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
1>c:\program files\microsoft sdks\windows\v6.0a\include\winnt.h(7818) : error C2146: syntax error : missing ‘;’ before identifier ‘Buffer’
1>c:\program files\microsoft sdks\windows\v6.0a\include\winnt.h(7818) : error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
1>c:\program files\microsoft sdks\windows\v6.0a\include\winnt.h(7818) : error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
|
于是将DX8的路径直接插到Include Path最前面,让程序编译时使用DX8中的winnt.h。问题解决(注意:为了不影响其他程序编译,建议在编译成功后将此次更改还原),如下:
最后又出现下面的错误,直接将其加入Ignore Specific Library解决问题!
|
1>Linking…
1>LINK : fatal error LNK1104: cannot open file ‘libci.lib’
|
为了调试方便,将两个项目的“Configuration Type"由Dll改为Lib
Hge源码包中包含若干sample(【tutorials】目录),所以下一步就是将这些代码并到同一个Solution中。
Done!!!
最近评论