存档

2009年12月 的存档

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

2009年12月8日 没有评论

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

                 

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

               
开发原理

 

 

 

                                                      
作者
Honghaier

                                        
QQ
285421210

                                     
日期: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横纵版与斜视角游戏地图开发原理

        
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度的,

 

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

                  
1.2

 

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

        

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

 

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

 

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

分类: 基础知识 标签: