您的位置:首页 > 其它

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

2009-12-08 14:58 956 查看
一个学生问的问题,借机做了个文档。发到博客。




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;
//图片索引
};






图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
= 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程序并完善SetTilePicture,AddElement等函数。通过鼠标消息处理来增加对应的TILE和元素,并通过键盘移动来改变屏幕的矩形。来制做一个可以漫游的简单的场景编辑器。


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



斜视角的地图:


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







图1.2


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



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


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


好了,基本的原理都讲解完了,看起来不难。但需要您亲自动手来实现它。开始吧,祝好运!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: