关于HGE/Box2D/C++编程的一些注意事项文档
1.用VS编译后的hge程序不能在其他机器上运行:
首先先确定hge.dll和bass.dll还有其他相关文件是否在hge程序的目录
下或者其找得到的地方.
然后请确认项目属性中公共语言运行库是否为关闭
具体做法项目属性(project properties)->配置属性(configuration p
roperties)->公共语言运行库(common language runtime support)
将该项关闭。
其次请确认Release的运行时库为MT模式
具体做法在之前提到配置属性中->C/C++->代码生成(code genera
tion)->运行时哭(runtime library)中修改。
2.使用hgeSprite类中的GetBoundingBox()时,注意其接受一个hgeRect
指针参数,将得到的sprite的boundingbox存储在该指针所指的对象中
因此,改指针指向的对象必须要先初始化后才能使用。
例如:
m_boundingbox = new hgeRect();
m_sprite = GetBoundingBox(m_posX, m_posY, m_boundingBox);
3.hgeSprite中的SetHotSpot的功用是设定该sprite的热点(锚点)。其接
受的参数a,b代表改热点据sprite坐上角的相对横,纵距离。设置热点
是初始化一个sprite过程中的一部分。之后再对该sprite进行处理时其
坐标就是其热点坐标。
例如:
hgeSprite* spr = new hgeSprite(0, 0, 0, 20, 20)//20x20
spr.SetHotSpot(10, 10);//Hot spot (10, 10) in center
4.当要将FrameFunc和RenderFunc封装到类里面时,注意用System_Se
tState是无法将类成员函数直接设为hge的FrameFunc和RenderFunc
的。 因为类成员函数属于一个具体的对象,不能直接从类调用他。
例如:
class GameState
{
public:
bool FrameFunc();
bool RenderFunc();
}
hge->System_SetState(HGE_FRAMEFUNC,
GameState::FrameFunc);//ERROR
hge->System_SetState(HGE_RENDERFUNC,
GameState::RenderFunc);//ERROR
代替的方法是将该类建立为Singleton,为其提供Static的FrameFunc
和RenderFunc来调用成员函数进行真正的工作。
例如
class GameState
{
public:
static instance;
static bool FrameFunc() {
return instance.doUpdate();
}
static bool RenderFunc() {
return instance.doRender();
}
private:
bool doUpdate();
bool doRender();
}
5.使用hge中Input系列函数时,区分GetKeyState和KeyDown的区别并
选择你需要的使用。KeyDown 用于侦测按键被按下的事件,适合于
监测单次按下键做出反应的情况。GetKeyState则是侦测某个键是否
被按下的状态。用于按下并保持按下按键进行操作情况比较合适。
6.我们一般用hge指针来操作hge。但是hge指针不需要是全局变量。由
于hge是Singleton,每次采用hgeCreate返回的指针都是指向同一个h
ge类。所以任何时候你需要用到hge中的功能就能用hgeCreate 来得
到一个指向hge的指针。
要记得使用release来释放hge。
7.尽量使用hgeResourceManager类。因为他它真的很好用。
如果没有使用他,那么注意在初始化texture/font时一定要确保hge
已经初始化过(System_Initiate已成功)。否则会产生运行时错误。
8.如果你将hgeSprite,hgeFont这些类封装在你自定义的类中,记得不
要把delete放在析构函数中。如果这样将导致游戏退出时出错。
将需要执行的delete操作提取出来放入一个函数中(如命名为Clear)
在合适的地方手动调用。
例如:
if (hge->System_Initiate())
{
RenderUnit::instance();//singleton
hge->System_Start();
RenderUnit::instance().clearup();//clearup includes deletes
}
9.HGE的Color格式为0xAARGGBB
其中A - alpha R -red G - green B - blue
但事实上传统C++颜色值表示为
0xBBGGRR
记得不要弄错。
10.HGE和Box2D混用时很容易采用一比一的单位, 即1个像素代表1个
Box2D中的一个单位。但这样是严重错误的。Box2D中1代表的单位
为1米。所以HGE中渲染一个30像素的正方形在Box2D中意味着一栋
楼房。Box2D在其文档中明确指出,Box2D只能模拟0,1至10米的物
体,对于超出范围以外的物体的物理模拟将会很不正常,一般表现
为速度慢,惯性大且难以加速到很大的速度。
所以一定不能在HGE和Box2D直接采用一比一单位比。
11.Box2D中如果为了给物体一个线性速度,比较好的方法是使用SetLi
nearVelocity()而不是ApplyImpulse()来设置冲量。
12.HGE和Box2D中获得的radius是相同的。所以下面语句能正确的将Bo
x2D模拟的情况反应到屏幕上。
objspr->RenderEx(dynbody->GetPosition().x,
dynbody->GetPosition().y, dynbody->GetAngle())
但是务必注意10条中的内容,一比一的单位制必然导致灾难性结果
13.HGE中sprite的淡入淡出效果可以通过SetColor不断更改sprite的颜色
值来做到。
对于一个带有自定义的texture的sprite,将其颜色设置为0xFFFFFFF
在一般的BlendMode设定下为最标准的值。如果更改为0x88FFFFFF
则正好是半透明状态。以此类推0x00FFFFFF时则为完全透明。
下面给出一个简单的渐变色的类的例子。
一个简单的办法是设置一个int的factor,将其乘以0x1000000对颜色
进行加减来控制其alpha值。
但要注意的是HGE中颜色值的类型是
typedef unsigned long DWORD
是非符号数。
如果用下面这种方式进行操作必然导致错误。
colorword -= factor*0x1000000;
if (colorword < 0x00FFFFFF) {
colorword = 0x00FFFFFF;
return false;//fade finished
}
return true;//fading
由于colorword是非符号树,if判断支内语句永远无法执行
可以改为下面的方法
DWORD delta = factor*0x1000000;
if (colorword < delta) {
colorword = 0x00FFFFFF;
return false;
} else {
colorword -= delta;
return true;
}
14.Box2D中一个Body的默认弹性系数是0,即不反弹。这个设定使得
在初期进行渲染测试时容易产生误解。
如果要设置一个Body使其能够反弹,首先检查其是否创建了Shape
然后看是否执行了SetMasses类语句,之后设置Restitution设置弹性
如果还是不能反弹则需检查该Body是否冻结(用IsFrozen()检查)
15.第10条中提到了不能在HGE和Box2D直接直接用一比一的单位制。
比较好的方法是预先建立你想要大小的Sprite,将Box2D模拟的结
果乘以系数加上偏移来渲染到屏幕上。
例如
//建立一个Box2D中的物件,尺寸为5m x 5m正方形
b2BodyDef dynBodyDef;
b2PolygonDef dynPolyDef;
dynPolyDef.SetAsBox(2.5f, 2.5f);
dynbody = world->CreateBody(&dynBodyDef);
dynbody->CreateShape(&dynPolyDef);
dynbody->SetMassFromShapes();
//根据上面Box3D中物件的尺寸创建Sprite,这里是1:10
dynspr = new hgeSprite(dyntex, 0, 0, 50.0f, 50.0f);
dynspr->SetHotSpot(25.0f, 25.0f);
//渲染,计算坐标要计入全局的偏移量和缩放量
dynspr->RenderEx(dynbody->GetPosition().x * GLOBSCALE
+ GLOBSHIFT,
dynbody->GetPosition().y * GLOBSCALE
+ GLOBSHIFT,
dynbody->GetAngle());
容易看出来对Body/Shape建立相应的Sprite时要用到Shape的尺寸
所以最好在Body的ShapeDef可见的地方就建立Sprite。
16.对于包装FrameFunc的类对象,慎用Singleton模式。
Singleton只要实例化后很难销毁重建。对于需要完成游戏重置的
情况会早成很大的麻烦。
含有RenderFunc的对象偶尔可以考虑用Singleton模式。
总的来说,程序进程中需要重置的对象最好都不要用Singleton。
EDIT:
后来发现对于这些对象还是尽量使用Singleton比较好
这样有一个比较可靠的可以全局取得的实例在
可以避免很多类似两个头文件需要互相包含的问题
对于重置问题
建议设置两个函数分别负责初始化和数据清除,在重置时可以先调用清
除函数,再进行初始化即可。注意要照顾到所有数据成员。
17.Box2D中实现"子弹时间"效果,只要动态修改world的timestep大小
即可。而且效果相当完美。
18.HGE中的卷轴问题,可以用Gfx_SetTransform解决。但是这个函数
貌似有点问题。
void Gfx_SetTransform(
float x = 0,
float y = 0,
float dx = 0,
float dy = 0,
float rot = 0,
float hscale = 0,
float vscale = 0
);
虽然每个都有默认参数但是如果根据源码中,如果忽略设置vscale
那么之前的参数都会被忽略。这应该是个bug。
所以如果要使用SetTransform必须将提供所有参数。
hge->Gfx_SetTransform(0,0, 10.0f, 10.0f, 0, 1.0f, 1.0f);
//前两个参数目前作用不明...
19.Box2D中隐藏的函数
有一些简单的计算函数在Box2D API文档中貌似是找不到的
比如b2vec2这个向量类的点乘和差乘操作,在b2Vec2类中没有相关描述
实际上在b2math.h中有定义一系列的相关函数,比如b2dot就是对两个向
量进行点乘。当你发现一些很常用的操作没有相关函数时不妨自己打开相
应的源代码翻看一下。
1.用VS编译后的hge程序不能在其他机器上运行:
首先先确定hge.dll和bass.dll还有其他相关文件是否在hge程序的目录
下或者其找得到的地方.
然后请确认项目属性中公共语言运行库是否为关闭
具体做法项目属性(project properties)->配置属性(configuration p
roperties)->公共语言运行库(common language runtime support)
将该项关闭。
其次请确认Release的运行时库为MT模式
具体做法在之前提到配置属性中->C/C++->代码生成(code genera
tion)->运行时哭(runtime library)中修改。
2.使用hgeSprite类中的GetBoundingBox()时,注意其接受一个hgeRect
指针参数,将得到的sprite的boundingbox存储在该指针所指的对象中
因此,改指针指向的对象必须要先初始化后才能使用。
例如:
m_boundingbox = new hgeRect();
m_sprite = GetBoundingBox(m_posX, m_posY, m_boundingBox);
3.hgeSprite中的SetHotSpot的功用是设定该sprite的热点(锚点)。其接
受的参数a,b代表改热点据sprite坐上角的相对横,纵距离。设置热点
是初始化一个sprite过程中的一部分。之后再对该sprite进行处理时其
坐标就是其热点坐标。
例如:
hgeSprite* spr = new hgeSprite(0, 0, 0, 20, 20)//20x20
spr.SetHotSpot(10, 10);//Hot spot (10, 10) in center
4.当要将FrameFunc和RenderFunc封装到类里面时,注意用System_Se
tState是无法将类成员函数直接设为hge的FrameFunc和RenderFunc
的。 因为类成员函数属于一个具体的对象,不能直接从类调用他。
例如:
class GameState
{
public:
bool FrameFunc();
bool RenderFunc();
}
hge->System_SetState(HGE_FRAMEFUNC,
GameState::FrameFunc);//ERROR
hge->System_SetState(HGE_RENDERFUNC,
GameState::RenderFunc);//ERROR
代替的方法是将该类建立为Singleton,为其提供Static的FrameFunc
和RenderFunc来调用成员函数进行真正的工作。
例如
class GameState
{
public:
static instance;
static bool FrameFunc() {
return instance.doUpdate();
}
static bool RenderFunc() {
return instance.doRender();
}
private:
bool doUpdate();
bool doRender();
}
5.使用hge中Input系列函数时,区分GetKeyState和KeyDown的区别并
选择你需要的使用。KeyDown 用于侦测按键被按下的事件,适合于
监测单次按下键做出反应的情况。GetKeyState则是侦测某个键是否
被按下的状态。用于按下并保持按下按键进行操作情况比较合适。
6.我们一般用hge指针来操作hge。但是hge指针不需要是全局变量。由
于hge是Singleton,每次采用hgeCreate返回的指针都是指向同一个h
ge类。所以任何时候你需要用到hge中的功能就能用hgeCreate 来得
到一个指向hge的指针。
要记得使用release来释放hge。
7.尽量使用hgeResourceManager类。因为他它真的很好用。
如果没有使用他,那么注意在初始化texture/font时一定要确保hge
已经初始化过(System_Initiate已成功)。否则会产生运行时错误。
8.如果你将hgeSprite,hgeFont这些类封装在你自定义的类中,记得不
要把delete放在析构函数中。如果这样将导致游戏退出时出错。
将需要执行的delete操作提取出来放入一个函数中(如命名为Clear)
在合适的地方手动调用。
例如:
if (hge->System_Initiate())
{
RenderUnit::instance();//singleton
hge->System_Start();
RenderUnit::instance().clearup();//clearup includes deletes
}
9.HGE的Color格式为0xAARGGBB
其中A - alpha R -red G - green B - blue
但事实上传统C++颜色值表示为
0xBBGGRR
记得不要弄错。
10.HGE和Box2D混用时很容易采用一比一的单位, 即1个像素代表1个
Box2D中的一个单位。但这样是严重错误的。Box2D中1代表的单位
为1米。所以HGE中渲染一个30像素的正方形在Box2D中意味着一栋
楼房。Box2D在其文档中明确指出,Box2D只能模拟0,1至10米的物
体,对于超出范围以外的物体的物理模拟将会很不正常,一般表现
为速度慢,惯性大且难以加速到很大的速度。
所以一定不能在HGE和Box2D直接采用一比一单位比。
11.Box2D中如果为了给物体一个线性速度,比较好的方法是使用SetLi
nearVelocity()而不是ApplyImpulse()来设置冲量。
12.HGE和Box2D中获得的radius是相同的。所以下面语句能正确的将Bo
x2D模拟的情况反应到屏幕上。
objspr->RenderEx(dynbody->GetPosition().x,
dynbody->GetPosition().y, dynbody->GetAngle())
但是务必注意10条中的内容,一比一的单位制必然导致灾难性结果
13.HGE中sprite的淡入淡出效果可以通过SetColor不断更改sprite的颜色
值来做到。
对于一个带有自定义的texture的sprite,将其颜色设置为0xFFFFFFF
在一般的BlendMode设定下为最标准的值。如果更改为0x88FFFFFF
则正好是半透明状态。以此类推0x00FFFFFF时则为完全透明。
下面给出一个简单的渐变色的类的例子。
一个简单的办法是设置一个int的factor,将其乘以0x1000000对颜色
进行加减来控制其alpha值。
但要注意的是HGE中颜色值的类型是
typedef unsigned long DWORD
是非符号数。
如果用下面这种方式进行操作必然导致错误。
colorword -= factor*0x1000000;
if (colorword < 0x00FFFFFF) {
colorword = 0x00FFFFFF;
return false;//fade finished
}
return true;//fading
由于colorword是非符号树,if判断支内语句永远无法执行
可以改为下面的方法
DWORD delta = factor*0x1000000;
if (colorword < delta) {
colorword = 0x00FFFFFF;
return false;
} else {
colorword -= delta;
return true;
}
14.Box2D中一个Body的默认弹性系数是0,即不反弹。这个设定使得
在初期进行渲染测试时容易产生误解。
如果要设置一个Body使其能够反弹,首先检查其是否创建了Shape
然后看是否执行了SetMasses类语句,之后设置Restitution设置弹性
如果还是不能反弹则需检查该Body是否冻结(用IsFrozen()检查)
15.第10条中提到了不能在HGE和Box2D直接直接用一比一的单位制。
比较好的方法是预先建立你想要大小的Sprite,将Box2D模拟的结
果乘以系数加上偏移来渲染到屏幕上。
例如
//建立一个Box2D中的物件,尺寸为5m x 5m正方形
b2BodyDef dynBodyDef;
b2PolygonDef dynPolyDef;
dynPolyDef.SetAsBox(2.5f, 2.5f);
dynbody = world->CreateBody(&dynBodyDef);
dynbody->CreateShape(&dynPolyDef);
dynbody->SetMassFromShapes();
//根据上面Box3D中物件的尺寸创建Sprite,这里是1:10
dynspr = new hgeSprite(dyntex, 0, 0, 50.0f, 50.0f);
dynspr->SetHotSpot(25.0f, 25.0f);
//渲染,计算坐标要计入全局的偏移量和缩放量
dynspr->RenderEx(dynbody->GetPosition().x * GLOBSCALE
+ GLOBSHIFT,
dynbody->GetPosition().y * GLOBSCALE
+ GLOBSHIFT,
dynbody->GetAngle());
容易看出来对Body/Shape建立相应的Sprite时要用到Shape的尺寸
所以最好在Body的ShapeDef可见的地方就建立Sprite。
16.对于包装FrameFunc的类对象,慎用Singleton模式。
Singleton只要实例化后很难销毁重建。对于需要完成游戏重置的
情况会早成很大的麻烦。
含有RenderFunc的对象偶尔可以考虑用Singleton模式。
总的来说,程序进程中需要重置的对象最好都不要用Singleton。
EDIT:
后来发现对于这些对象还是尽量使用Singleton比较好
这样有一个比较可靠的可以全局取得的实例在
可以避免很多类似两个头文件需要互相包含的问题
对于重置问题
建议设置两个函数分别负责初始化和数据清除,在重置时可以先调用清
除函数,再进行初始化即可。注意要照顾到所有数据成员。
17.Box2D中实现"子弹时间"效果,只要动态修改world的timestep大小
即可。而且效果相当完美。
18.HGE中的卷轴问题,可以用Gfx_SetTransform解决。但是这个函数
貌似有点问题。
void Gfx_SetTransform(
float x = 0,
float y = 0,
float dx = 0,
float dy = 0,
float rot = 0,
float hscale = 0,
float vscale = 0
);
虽然每个都有默认参数但是如果根据源码中,如果忽略设置vscale
那么之前的参数都会被忽略。这应该是个bug。
所以如果要使用SetTransform必须将提供所有参数。
hge->Gfx_SetTransform(0,0, 10.0f, 10.0f, 0, 1.0f, 1.0f);
//前两个参数目前作用不明...
19.Box2D中隐藏的函数
有一些简单的计算函数在Box2D API文档中貌似是找不到的
比如b2vec2这个向量类的点乘和差乘操作,在b2Vec2类中没有相关描述
实际上在b2math.h中有定义一系列的相关函数,比如b2dot就是对两个向
量进行点乘。当你发现一些很常用的操作没有相关函数时不妨自己打开相
应的源代码翻看一下。