2D横纵版与斜视角游戏地图开发原理

长平狐 发布于 2013/03/19 18:40
阅读 757
收藏 1

    一个学生问的问题,借机做了个文档。发到博客。

                 

            2D横纵版与斜视角游戏地图

                开发原理

 

 

 

                                                       作者 Honghaier

                                         QQ285421210

                                      日期:2009-12-8

 

 

 

 

开发前提

 

 

1.假设您已经常握了C++语言,并能够熟练使用VC++开发工具。

 

2.假设您已经能够成功的封装了一个C2DPicture类。它具有以下函数。

 

//载入图片文件,成功返回一个索引值,失败返回-1

Int  LoadImage(const char*  szFileName);

           

     //取得纹理

         LPDIRECT3DTEXTURE9          GetTexture();

 

     //取得图片信息

     Int                                                GetImageWidth();

          Int                                                GetImageHeight();

 

     如果您无法做出这样的类,可以参考D3D的可变顶点格式D3DFVF_XYZW,有很多例子。

     3. 出于效率的考虑,我认为您应该写一个管理器来管理这些C2DPicture类。假设叫C2DPictureManage.它具有以下函数.

Public:

     //载入所有图片,在这里你可以加载图片

     BOOL   Init();

          //取得对应的C2DPicture指针

         C2DPicture*    GetPicture(int PictureIndex);

          //在指定位置显示指定图片

          Void        ShowPicture(int PictureIndex,int Left,int Right,int Width,int Height);

 

Private:

          //在指定位置渲染图片

         Void           Render(LPDIRECT3DTEXTURE9       pTexture int Left,int Right,int Width,int Height);

 

         注:为什么不在C2DPicture类中做这个函数呢,因为出于效率的考虑,我们可以在C2DPictureManage定义四个顶点就行了,在ShowPicture函数中通过PictureIndex来获取C2DPicture对象指针。然后取得纹理和图片通过Render进行D3D渲染;

 

原理简述:

     2D的横纵版地图:

 

      整个地图由横,纵,二个方向的TILE块组成。每个TILE块即是一层或几层图片。具体多少层看您的游戏设置了,一般来说,一个TILE里可以放三层,最底层是背景,第二层是远建筑,第三层是近建筑。当然,这只是基于拼合关系的TILE图,还有大量的草,树,星星什么的。是不基于拼合关系的。

 

      所以您需要好好设计数据结构,比如这样,分为两种结构,

 

1.基于拼合的TILE

Struct      SMapTile

{

         Int    mPosX;                       //在地图中绝对位置X

         Int  mPosY;                       //在地图中绝对位置Y

         Int    Picture[3];                  //三层图片的ID
}

;

2.不固定的地图元素

Struct       SMapElement

{

         Int    mPosX;                       //在地图中绝对位置X

         Int  mPosY;                       //在地图中绝对位置Y

         Int    mPictureIndex;         //图片索引

};

 

 

 

         1.1

 

         在这里,我引入了一个典型的2D横纵版场景。每个TILE45 * 32 像素的。我们这里的TILE三层为别是背景墙,斜桥,近墙,放在SMapTile结构中。不固定元素有草,灯光台。放在SMapElement结构中。

 

 

         这只是一屏。实际上一个游戏场景需要至少几十屏,也可能上百屏。所以这一屏,只是整个场景中的一小区块。

        

         所以一般我们会定义一个地图类CMap,它可能有以下成员

         Private:   

        

         Int                                                   m_MapWidth;                             //地图的TILE横向数量

         Int                                                   m_MapHeight;                           //地图的TILE纵向数量

         Int                                                   m_TileWidth;                               //Tile的像素宽

         Int                                                   m_TileHeight;                             //Tile的像素高                                   

         SMapTile*                                    m_pTileArray;                             //TILE数组指针

         Vector<SMapElement>             m_MapElementVec[3];             //介与各层间的元素数组

         Public:

        

         BOOL                 CreateNewMap(int vWidth,int vHeight,);       //创建地图,动态为TILE数组申请内存并初始化,如m_pTileArray = new SMapTile[vWidth*vHeight];

 

         VOID                  SetTilePicture(int vTileX,int vTileY,int vLayerIndex,int vPictureIndex);   //为指定的TILE的指定层设置图片索引

 

         VOID                  AddElement(int vLayerIndex,int vPosX,int vPosY,int vPictureIndex);  //放入元素

        

         VOID                  RenderMap(int vLeft,int vTop,int vWidth,int vHeight);                     //显示场景中处于vLeft, vTop, vWidth, vHeight区块的地图。         这个理所应当就是屏幕矩形了。

 

        

         VOID                  RenderElement(int  vLayerIndex, int vLeft,int vTop,int vWidth,int vHeight);       //显示处于对应矩形中的元素

 

         那么,怎么能够显示任意区块呢?这个就是2D场景漫游了。

 

         首先,我们先创建一个地图,假设设为200*100TILE横纵向数量。并设置TILE像素高,宽为48*48。这样我们就知道场景有多大了。一个屏幕一般为800*600大小。

200*48 * 100 *48 / (800*600) = 96.   嗯,貌似还可以。

 

         我们在RenderMap函数中应能够正确的处理TILE和元素的渲染。下面是实现过程。

 

 

我们假设有全局对象C2DPictureManage             G_PictureManage;

 

VOID                  CMap::RenderMap(int vLeft,int vTop,int vWidth,int vHeight)

{

        

         Int             TileX        =  vLeft / m_TileWidth ;                 //计算格子位置

         Int             TileIY       =  vTop / m_ TileHeight;

                 

         Int             OffSetX   =  vLeft% m_TileWidth;                   //计算偏移

         Int             OffSetY     =  vTop % m_ TileHeight;                         

 

//计算从哪开始贴图

         Int             Left           =  OffSetX > 0 ? (OffSetX  -  m_TileWidth) : 0;

         Int             Top            =  OffSetY > 0 ? (OffSetY  -  m_TileHeight) : 0;

 

//计算总共多少TILE

         Int             TileNumX          = OffSetX > 0 ? (vWidth / m_TileWidth +1) : (vWidth / m_TileWidth;)

 

         Int             TileNumY          = OffSetY > 0 ? (vHeight / m_ TileHeight +1) : (vHeight / m_ TileHeight;)

 

         //背景

         For(int     I = 0  ;  I < TileNumY; I ++)

         For(int      J = 0         ;  j < TileNumX ; j ++)

         {

                   Int  TileIndex = I * m_MapWidth + j;

                   Int    PictureIndex  = m_pTileArray[TileIndex]. Picture[0];

                   If(PictureIndex  >= 0)

                   {

                            G_PictureManage. ShowPicture (PictureIndex, Left + j* m_TileWidth , Top + i* m_TileHeight, m_TileWidth, m_TileHeight);

                   }

         }

 

         //背间与远建筑层间元素

         RenderElement(0, vLeft, vTop, vWidth, vHeight); 

        

         //远建筑层

         For(int     I = 0  ;  I <  TileNumY; I ++)

         For(int      J = 0         ;  j < TileNumX ; j ++)

        

                   Int  TileIndex = I * m_MapWidth + j;

                   PictureIndex  = m_pTileArray[TileIndex]. Picture[1];

                   If(PictureIndex  >= 0)

                   {

                            G_PictureManage. ShowPicture (PictureIndex, Left + j* m_TileWidth , Top + i* m_TileHeight, m_TileWidth, m_TileHeight);

                   }

         }

        

         //远建筑层与近建筑层间元素

         RenderElement(1, vLeft, vTop, vWidth, vHeight); 

                  

         //近建筑层

         For(int     I = 0  ;  I < TileNumY; I ++)

         For(int      J = 0         ;  j < TileNumX ; j ++)

         {

                     Int  TileIndex = I * m_MapWidth + j;

 

                   PictureIndex  = m_pTileArray[TileIndex]. Picture[2];

                   If(PictureIndex  >= 0)

                   {

                            G_PictureManage. ShowPicture (PictureIndex, Left + j* m_TileWidth , Top + i* m_TileHeight, m_TileWidth, m_TileHeight);

                   }

         }

 

         //近建筑层上元素

         RenderElement(2, vLeft, vTop, vWidth, vHeight); 

}

 

 

//显示处于对应屏幕矩形中的元素

VOID                  CMap::RenderElement(int  vLayerIndex, int vLeft,int vTop,int vWidth,int vHeight)

{       

         //取得数量

         Int    size = m_MapElementVec[vLayerIndex].size();

 

         For(int I = 0 ; I < size ; i++)

         {

                   //图片索引

                   Int PictureIndex = m_MapElementVec[vLayerIndex][i]. mPictureIndex;

                   If(PictureIndex > 0)

                   {       

                            C2DPicture*    tpPicture = G_PictureManage.GetPicture(PictureIndex);

                            If(tpPicture)

                            {       

 

                                     Int    Left = m_MapElementVec[vLayerIndex][i].mPosX;

                                     Int    Top = m_MapElementVec[vLayerIndex][i].mPosY;

                                     Int    Right = Left + tpPicture-> GetImageWidth();

                                     Int    Bottom = Top + tpPicture-> GetImageHeight ();

                                    

                                     //判断与格子是否有交集

 

                                     If(Left > (vLeft + vWidth))continue;

                                     If(Top > (vTop + vHeight))continue;

                                     If(Right < (vLeft))continue;

                                     If(Bottom < (vTop))continue;

 

                                     //如果有交集,则渲染

 

G_PictureManage. ShowPicture (PictureIndex, m_MapElementVec[vLayerIndex][i].mPosX - Left , m_MapElementVec[vLayerIndex][i].mPosY  - Top);

 

                            }
                   }
         }

}

 

 

                   以上是核心显示代码,为了测试,您可以写一个MFC程序并完善SetTilePictureAddElement等函数。通过鼠标消息处理来增加对应的TILE和元素,并通过键盘移动来改变屏幕的矩形。来制做一个可以漫游的简单的场景编辑器。

 

                   OK,2D横纵版的场景原理讲述完了。

 

 

         斜视角的地图:

 

                   斜视角地图一般分为两类:45度角和30度角的。理解起来就是45度角的TILE是正方形, 30度角的TILE是宽高比为21.   45度角的斜视角拼合会感觉眼角立体感陡一些,用得较少,一般都是30度的,

 

                  

                   1.2

 

                   如图所示,所有的TILE中图片都是斜30度的。

        

                   其实从绘制上与之前讲的横纵绘制并没有什么不同,算法不需要什么大的改动。但是在场景移动时。加上向左上30度移动。向左下30度移动。向右下30度移动,向右上30度移动。这样就能产生立体感了。

 

                   一般视角会随着主角人物的移动来漫游,人物移动,带动屏幕矩形移动。如果要实现斜视角。则一般为人物八个方向的图片。然后在移动时通过人物的位置来取得屏幕在整个地图的矩形位置,然后绘制地图就行了。

 

                   好了,基本的原理都讲解完了,看起来不难。但需要您亲自动手来实现它。开始吧,祝好运!


原文链接:http://blog.csdn.net/honghaier/article/details/4964527
加载中
返回顶部
顶部