0
点赞
收藏
分享

微信扫一扫

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


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


                 

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

                开发原理


                                                       作者 Honghaier

     

                                      日期: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;         图片索引
};

 

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

         图1.1

 

         在这里,我引入了一个典型的2D横纵版场景。每个TILE是45 * 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*100的TILE横纵向数量。并设置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          
 
         Int             TileNumY          
 
         //背景
         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 ++)
         { 
  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 ++)
         {
  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程序并完善SetTilePicture,AddElement等函数。通过鼠标消息处理来增加对应的TILE和元素,并通过键盘移动来改变屏幕的矩形。来制做一个可以漫游的简单的场景编辑器。

 

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

 

 

         斜视角的地图:


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

 

                  

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

                   图1.2

 

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

        

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

 

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

 

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

举报

相关推荐

0 条评论