游戏开发论坛

 找回密码
 立即注册
搜索
查看: 14685|回复: 32

自己写游戏引擎——Get your hands dirty!!! (01)

[复制链接]

22

主题

191

帖子

217

积分

中级会员

Rank: 3Rank: 3

积分
217
QQ
发表于 2006-8-7 07:29:00 | 显示全部楼层 |阅读模式
2006年08月07日
自己写游戏引擎——Get your hands dirty!!! (01)


简单说明:欢迎转载(注明出处),请勿用于商业用途。
写了几个月的游戏引擎,有一些想法需要总结结一下,也想和同我水平差不多的朋友,或者比我水平还要菜的cn们分享一下,高手莫笑~~
这里的想法是,尽量和大家分享我的过程,我写的东西很烂(我的确这样觉得),我会在文中写下来我觉得还存在问题的地方,需要改进的地方,或者还没有想到很好解决的地方;希望同大家相互讨论,也非常欢迎高手们的建议和批评.Just for FUN! OK.
(ps:文中的代码都是片断,these don’t compile,如果你想要部分代码,可以和我联系,在引擎完成得差不多的时候我会邮给你,我的邮箱:xjyhust@gmail.com or xjyhust@126.com. )

       目标是写一个多渲染器的游戏引擎,先来谈其中的图形渲染部分,毕竟这个是关键,如果你想了解物理,网络或者其他的内容,很遗憾,这里没有(也许要等到很后面的了)。可以看一下我之前写的两篇blog,《游戏引擎中多渲染器的设计与实现》(1,2),里面讲到了用动态链接库实现的一种的方法,这里就不复习了,假设你已经完成了前面最基本的实现。
       引擎的核心部分是一切的前提,所以我们从这里开始。我强烈建议你在开始动手之前先看一下常用的3d模型的格式的解析——因为引擎的底层的基本数据类型的设计是非常重要,而且在后面是相当难以改动的——看一下常用的模型格式的解析,可以帮助你设计合理的底层数据类型(这里指的是像 纹理,材质,三角形等的表示)。先以MilkShape 3D的格式MS3D为例。在他们的网站上,你可以下到一个C++解析组件,先看头文件中的数据类型:
typedef struct
...{
    byte    flags;                                      // SELECTED | SELECTED2 | HIDDEN
    float   vertex[3];                                  //
    char    boneId;                                     // -1 = no bone
    byte    referenceCount;
} ms3d_vertex_t;

typedef struct
...{
    word    flags;                                      // SELECTED | SELECTED2 | HIDDEN
    word    vertexIndices[3];                           //
    float   vertexNormals[3][3];                        //
    float   s[3];                                       //
    float   t[3];                                       //
    byte    smoothingGroup;                             // 1 - 32
    byte    groupIndex;                                 //
} ms3d_triangle_t;

typedef struct
...{
    word edgeIndices[2];
} ms3d_edge_t;

typedef struct
...{
    byte            flags;                              // SELECTED | HIDDEN
    char            name[32];                           //
    word            numtriangles;                       //
    word*            triangleIndices;                    // the groups group the triangles
    char            materialIndex;                      // -1 = no material
} ms3d_group_t;

typedef struct
...{
    char            name[32];                           //
    float           ambient[4];                         //
    float           diffuse[4];                         //
    float           specular[4];                        //
    float           emissive[4];                        //
    float           shininess;                          // 0.0f - 128.0f
    float           transparency;                       // 0.0f - 1.0f
    char            mode;                               // 0, 1, 2 is unused now
    char            texture[128];                        // texture.bmp
    char            alphamap[128];                       // alpha.bmp
} ms3d_material_t;

       从代码中可以看出,Texture和Material都有name,都有fileName(指的是贴图文件名),Material中有4中颜色(这和图形学里面的物理意义也是一致的),然后就是Texture可以是alpha贴图,有属性transparency,这些都可以作为我们设计的参考。我们就停在这里,在后面我们还会看到这个经典的格式是怎样影响了我们的渲染批次的设计的。
     开始建立基础的数据类型:
    typedef struct _colorStruct
    ...{
        float r;
        float g;
        float b;
        float a;
    }UHECOLOR;

    typedef struct _materialStruct
    ...{
        UHECOLOR Diffuse;
        UHECOLOR Ambient;
        UHECOLOR Specular;
        UHECOLOR Emissive;
        float Power;
    }UHEMATERIAL;

    typedef struct _textureStruct
    ...{
        bool    bAlpha;
        float   fAlpha;
        char    chName[128];
        void    *pData;
        WORD    texType;
        WORD    texUsage;
    }UHETEXTURE;

    这里很奇怪的是,我在Texture里面用了一个void*。恩,这个是用来存储不同API中的纹理。比如,如果是用的DX,我会一直把这个void*看成LPDIRECT3DTEXTURE9*。在引擎中,我会大量的用到指针间的静态转换(非运行时)(hard core c++高手看到我的代码会说应该用static_cast,而不是用c语言里面的(typename)(…)。恩,我同意,只是有时候我忘了,或者懒了)。
    有很多的引擎,会将Texture单独的抽象成一个类,有相应的行为(成员函数);但是,我觉得,Texture的实质是一堆数据,既然Texture Manager(纹理管理器,简记为TextureMng)是必要的,那么单独的Texture的类的设计似乎没有太多的必要,我们有TextureMng的管理,这样似乎足够了,我暂时不想把事情搞得太复杂。
(ps:面对对象是很好的方法,但是引擎中的很多“东西”的实质还是数据的集合,在把他们变成类之前,考虑一下于他们相关的操作,考虑一下用类实现一些很小的数据体的时候效率的影响。。。像Quake3和Ogre就是两个设计的极端,是不是有时候结合两者会好一些?)
       还有很多的数据类型要写,比如很多的3D中的数学类,像Vector3,Matrix4,先把他们放一下。我比较喜欢迭代开发的感觉——用比较短的周期,作出简单的可运行的程序,然后在改进。好,我们就以dx sdk里面的例子开始,先实现一个Vertex的例子。
       我们需要定义一个VertexBuffer的抽象类,来给GL和DX提供公共的接口。
//File: UHEVertexBuffer.h
#ifndef _INCLUDE_UHEVERTEXBUFFER_H
#define _INCLUDE_UHEVERTEXBUFFER_H

namespace UHEngine
...{
    class _UHE_Export VertexBuffer
    ...{
    public:
        // init and malloc a bunch of memory.
        virtual void    Create( UINT length, DWORD dwFVF, DWORD dwUsage, DWORD dwPoolType )     = 0;

        // lock the whole buffer.
        // then can write to the buffer using GetLockedData() .
        virtual void    Lock()                                                                 = 0;

        // unlock the locked data.
        virtual void    Unlock()                                                             = 0;

        // delete .
        virtual void    Release()                                                             = 0;
        
        // return the vertex buffer.
        // ex. in dx, this would be trans to LPDIRECT3DVERTEXBUFFER9 .
        virtual void    *GetVertexBuffer()                                                     = 0;
        
        // return the addr of buffer after method Lock() .
        virtual void    *GetLockedData()                                                     = 0;
        
        // return FVF type of vertex in the buffer.
        virtual DWORD    GetFVF()                                                             = 0;
        
        // return the length of buffer( actual length in memory ).
        virtual UINT    GetLength()                                                             = 0;
        
        // return Usage of vertex in the buffer.
        virtual DWORD    GetUsage()                                                             = 0;
        
        // reutrn pool management type.
        virtual DWORD   GetPoolType()                                                         = 0;
    };
}
#endif
(ps:多渲染器引擎中,用抽象类(纯虚类)是一种实现中的设计,即使用不同的API的底层实现,对于用户来说,都是同一接口)
       在DX9渲染器中我们加入文件,UHEDX9VertexBuffer.h和UHEDX9VertexBuffer.cpp。
//File: UHEDX9VertexBuffer.h
#ifndef _INCLUDE_UHEDX9VERTEXBUFFER_H
#define _INCLUDE_UHEDX9VERTEXBUFFER_H

#include "UHEDX9RenderBase.h"
#include "UHEVertexBuffer.h"

namespace UHEngine
...{
    class _UHE_Export DX9VertexBuffer : public VertexBuffer
    ...{
    public:
        DX9VertexBuffer( LPDIRECT3D9 &pDX9, LPDIRECT3DDEVICE9 &pDevice )            ;
        ~DX9VertexBuffer()                                                            ;

        void    Create( UINT length, DWORD dwFVF, DWORD dwUsage, DWORD dwPoolType ) ;
        void    Lock()                                                                ;
        void    Unlock()                                                            ;
        void    Release()                                                            ;
        
        void    *GetVertexBuffer() ...{ return (void*)m_pDXVertexBuffer; }                ;
        void    *GetLockedData() ...{ return (void*)m_pData; }                            ;
        DWORD    GetFVF() ...{ return m_dwFVF; }                                        ;
        UINT    GetLength() ...{ return m_length; }                                    ;
        DWORD    GetUsage() ...{ return m_dwUsage; }                                    ;
        DWORD   GetPoolType()...{ return m_dwPoolType; }                                ;   
                                                                                    
    private:
        LPDIRECT3D9                    m_pDX9              ;
        LPDIRECT3DDEVICE9            m_pDevice          ;

        LPDIRECT3DVERTEXBUFFER9        m_pDXVertexBuffer ;
        void                        *m_pData          ;
        bool                        m_bIsLocked          ;

        DWORD                        m_dwFVF              ;
        UINT                        m_length          ;
        DWORD                        m_dwUsage          ;
        DWORD                        m_dwPoolType      ;
    };
}
#endif
       在UHEDX9VertexBuffer.cpp中,完成这些实现(看一下dx sdk,没有什么值得多说的了)
       先写到这里,明天继续。。。

22

主题

191

帖子

217

积分

中级会员

Rank: 3Rank: 3

积分
217
QQ
 楼主| 发表于 2006-8-7 07:30:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

直接粘贴太乱了,懒得整理了,看原文比较好:
我的blog:
blog.csdn.net/xjyhust

33

主题

669

帖子

669

积分

高级会员

Rank: 4

积分
669
QQ
发表于 2006-8-7 19:53:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

很好,先记下,有空一定去看看  

0

主题

199

帖子

199

积分

注册会员

Rank: 2

积分
199
发表于 2006-8-7 21:47:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

精神可嘉,望继续发扬

30

主题

357

帖子

388

积分

中级会员

Rank: 3Rank: 3

积分
388
QQ
发表于 2006-8-7 22:21:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

我看过啦!楼主我爱你!跟我的程序思路太象啦,我特别赞成楼主杜绝滥用OO的观念,有些东西就是数据结构,偏偏有那么写人非得加几个方法上去才看的舒服。

0

主题

202

帖子

202

积分

中级会员

Rank: 3Rank: 3

积分
202
发表于 2006-8-7 22:38:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

搞不懂

14

主题

131

帖子

136

积分

注册会员

Rank: 2

积分
136
发表于 2006-8-7 22:59:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

很欣赏你的建立统一抽象接口,这种构建方式很好,灵活性很大,mesh,等都可以采用这种方式。

0

主题

3

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2006-8-7 23:13:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

楼主写得不错吗,在下初学3D游戏编程,还请多多指教

0

主题

7

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2006-8-8 08:36:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

写的不错~~
把你的DEMO发给我研究研究~~
我的油箱twin888@sina.com~~
  我现在在搞3D引擎底层~~的GUI
   用ORGER 引擎的底层代码~CEGUI开发的~~不知道楼主还研究过CEGUI的哦~

0

主题

7

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2006-8-8 08:44:00 | 显示全部楼层

Re: 自己写游戏引擎——Get your hands dirty!!! (01)

我是用dircet~~不是OPENGL
  
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-25 12:44

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表