|
|

楼主 |
发表于 2005-6-30 13:21:00
|
显示全部楼层
Re:我的游戏开发总结[3]
这里比较完整系统的介绍一下我的图形模块部分,网络部分已经有介绍了,也有例子
1、游戏的框架
游戏调用gc_init初始化,调用gc_destory释放内存,下面的代码是标准调用
// 这里进行系统初始化,返回一个根对象
LPOBJ pRoot = gc_init( hInstance, hwndMain, true );
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
while ( msg.message != WM_QUIT )
{
if ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
gc_update( ); // 这里是用来处理渲染和输入的函数
}
}
gc_destory( );
要注意的是gc_init会返回一个根对象,所有的渲染对象都有一个父对象,最终的父对象就是这个根,也就是说在我的图形模块里,游戏渲染对象是用树来组织的。
2、资源文件
我对每个资源文件进行命名,函数有两个
// 从文件中生成一个名为sName的图像资源
int GC_API gc_img_file( LPCSTR sName, LPCSTR sFile, int x, int y, int w, int h, DWORD dwColor );
// 从内存中生成一个名为sName的图像资源
int GC_API gc_img_memory( LPCSTR sName, BYTE * imgBuf, int nSize, int x, int y, int w, int h, DWORD dwColor );
参数sName是给资源文件取的名字,比如“背景图片”,“地图资源2001”等都可以,只要保证不同的资源取不同的名字即可,否则,如果取相同的名字就是用后一个文件Update前一个。
参数x, y, w, h分别是图片坐上角坐标和大小,因为有时候我们可能只想读取图片文件的一部分,如果读取整个图片文件又不知道大小,w, h参数给0就可以了,dwColor是需要过滤的颜色,如果不需要过滤,dwColor=0x0,注意这是带alhpa的过滤,如果是过滤黑色,dwColor=0xFF000000
之所以有2个函数,第二个函数是从内存中的图片缓冲中生成资源,因为有时候我们需要给图片加密,不能直接从文件中读取,可以先在内存中解密,然后直接调用gc_img_memory函数,要注意的是,这里从内存中生成资源不是支持自定义的图片格式,内存中的数据也必须是通用的图片文件格式。
3、渲染对象
图形部分最重要的是对资源文件和渲染对象的组织,那么渲染对象怎么来实现呢。
对于众多的游戏对象,比如:地图,精灵,NPC,角色,界面UI中的按钮等等,可能不同的游戏不一样,有2D的,3D的,这个没有关系,这些对象最终是一个渲染对象,最终是要送到渲染队列中去的。
我们只要获得这些对象实例的指针,调用一个渲染函数来挨个渲染他就可以了
因此,我定义了一个基类cls_obj,这个基类是一个虚基类,不能直接实例化的。我们在使用这个类的时候必须先进行派生,实现我们自己的功能,当然,也可以不用派生,我已经实现了一个派生类cls_obj_imp,可以直接new一个cls_obj_imp。
而且,由于我的框架需要自己来管理这些渲染对象的指针,那么不能让外部来删除这些对象,因为如果外部删除了,我的渲染队列不知道,还在继续引用该对象的指针就会出错,而且有时候,我们也容易用完了不释放,那么没关系,这一切我来帮你做,只要在派生cls_obj的时候,加上宏IMP_DELETE就万事OK了,至于原因,我前面的论述中已经说明了。那么我们如果要删除渲染对象怎么办呢,调用delete是不行的了,只能调用Destory函数。
下面是一个简单的派生类
class cls_obj_imp : public cls_obj
{
MP_DELETE
public:
cls_obj_imp( cls_obj * pFather ) : cls_obj( pFather ) { }
};
上面说过,我们的每个对象都是有父对象的,在构造的时候就必须指定父对象,我们的根对象就是通过gc_init返回的。
4、实现
渲染对象有了,比如我们要实现一个按钮(我的例子中有代码)
class CMButton : cls_obj
{
CTSS m_sMethod;
LPOBJ m_pCaller;
CAGV m_agvClick;
IMP_DELETE
public:
CMButton( cls_obj * pFather, LPCSTR sBtName, int x, int y, int w, int h ) : cls_obj( pFather )
{
SetRect( x, y, w, h );
// 这里是按钮的图片,刚才说了,我们对每个图片资源都取了名字的
// sBtName就是图片资源的名字
// 后面的3,1是表示这个图片是3列1行,每行每列的大小是w,h,这样一共有3帧
SetImage( sBtName, 0, 0, w, h, 3, 1 );
// 默认就是第1帧图片,索引是0
}
// 这个函数就是用来设置按钮Click事件调用的参数
void SetClick( LPOBJ pCaller, LPCSTR sMethod, CAGV& agvClick )
{
m_sMethod = sMethod;
m_pCaller = pCaller;
m_agvClick = agvClick;
}
virtual void OnLBDown( int nFlag, int x, int y ) // 左键按下
{
// 鼠标按下去的时候显示第3帧的图片
SetFrame( 2 );
}
virtual void OnLBUp( int nFlag, int x, int y ) // 左键弹起
{
// 鼠标弹起也就是按钮Click事件,那么我们要处理这个Click事件
// m_pCaller是处理事件的对象,m_sMethod是该对象的方法名称,m_agvClick是方法的参数
if ( m_pCaller && !m_sMethod.is_empty( ) )
m_pCaller->Call( m_sMethod, m_agvClick );
// 鼠标弹起的时候显示第2帧的图片
SetFrame( 1 );
}
virtual void OnMouseOver( int nFlag, int x, int y ) // 鼠标移动
{
// 鼠标移过按钮的时候显示第2帧的图片
SetFrame( 1 );
}
virtual void OnMouseLeave( int nFlag ) // 鼠标离开
{
// 鼠标离开按钮的时候显示第1帧的图片
SetFrame( 0 );
}
};
这样一个按钮就做好了,用的时候
gc_img_file( "按钮0001", "rc/btn.png", 0, 0, 0, 0, 0xFF00FF00 ); // 绿色ColorKey
CMButton * pBtn = new CMButton( this, "按钮0001", 360, 320, 58, 22 );
IMP_METHOD(OnBtnClick); // 注册一个方法
pBtn->SetClick( this, "OnBtnClick", agv );
|
|