游戏服务器之网格视野列表

来源:互联网 发布:喜马拉雅fm mac 编辑:程序博客网 时间:2024/05/21 07:46

网格视野列表的同步实现的是客户端于服务器之间的可见的实体的视野数据同步。

视野列表还可以作为技能攻击的检索列表。

设计上:

屏幕大小不超过1900 *1900像素,格子大小设定为128*128 ,视野范围定为16 * 16 (1900 / 128 + 2=16)的大小。

地图上按每个格子一个场景实体列表(npc和玩家),只要场景实体格子位置改变,则修改场景实体列表。

角色移动,或者视野内的npc移动,会更新角色视野列表,即比较角色视野范围场景实体与当前该角色的视野实体列表的不同,发送场景实体出现或消失的消息到客户端。角色移动(不管是否切网格)时会广播移动消息到视野内的玩家实体。


缺点:

(1)后端跟前端一样使用像素坐标(这里的网格的概念是作为屏而存在的,所以视野的同步跟传统的9屏不一样,目前是设置成16*16格子)。

(2)切换网格次数会较频繁(跟9屏比较)。

(3)只要有视野实体切网格就需要更新视野列表到客户端(只要角色移动就会检查),更新较频繁(跟9屏比较)。


与9屏同步对比:

9屏同步,使用的每个小格子为32*32 像素,一个屏的大小可以设置成屏幕大小的1/4,即23(1440/32/2=22.5)*14(900/32/2=14)格子。

只有切屏时才获取9屏视野实体。角色、npc在移动(不管是否切屏)广播移动消息。


优化:

后端只使用格子坐标。

由格子组成屏,切屏才更新视野实体。


本文内容:

(1)角色的视野列表

(2)更新视野列表并发送消息

(3)如果角色消失(死亡或者下线或换地图)则会从地图的网格实体列表里移除


(1)角色的视野列表

class CDoer : public CEntity
{

...

CBList<ViewStruct> m_ViewList;//视野列表

//可视范围(网格范围)(可视格子宽高:m_nViewArea = 1900 / 128 + 2=16个格子的宽和高)视野范围是一个屏幕的大小再加两个格子(边缘)

//每个格子的大小是128*128像素

int m_nViewArea;

};


(2)更新视野列表并发送消息

void CDoer::updateViewList()
{
if (!m_pScene)
return ;
CMapGrid* mapGrid = m_pScene->m_pMapGrid;
if (!mapGrid)
return ;


//重置更新时间(500ms更新一次)
m_dwNextUpdateViewTime = g_LogicEngine->getTickCount() + 500;
bool changeFlag = false;


//遍历entity对象的可视列表,设置列表中所有对象的mask为0离开视野
ViewStruct *vList = m_ViewList;
for (INT_PTR i = m_ViewList.count()-1; i > -1; --i)
{
if (vList[i].mask == VIEW_STATE_LEAVE)
{
if (m_btType == ePlayer)
sendMsg(PM_ACTOR_DISAPPEAR, 0, vList[i].pEntity->m_EntityId.llid, 0, 0, 0);//发送玩家视野实体的离开消息
m_ViewList.remove(i);
}
else vList[i].mask = VIEW_STATE_LEAVE;
}


//遍历可视范围中所有块的所有对象,把每个对象都与当前可视列表对比,
//已存在则设置mask为已存在,不存在则设置mask为进入视野并添加进当前可视列表中
CBList<CEntity*> eList;

int l = GX(m_nX) - m_nViewArea / 2;
int t = GY(m_nY) - m_nViewArea / 2;
int r = l + m_nViewArea;
int b = t + m_nViewArea;

if (mapGrid->getEntityList(l, r, t, b, eList, this) > 0)//获取地图上的角色视野矩形范围内的实体并添加新的实体到玩家的视野列表
{
int nViewIdx = -1;
CEntity** pEList = eList;
for (INT_PTR nEList = eList.count()-1; nEList > -1; --nEList)
{

//检查是否已在视野列表里了
nViewIdx = getViewIndex( pEList[nEList]);//查找是否在当前对象的可视列表中,存在则返回所在的索引,不存在则返回-1
if (-1 != nViewIdx)
{
vList[nViewIdx].mask = VIEW_STATE_STAY;//停留状态(表示处理过消息了)
}
else//不在则加入
{
ViewStruct view;
view.mask = VIEW_STATE_ENTER;
view.pEntity = pEList[nEList];
m_ViewList.add(view);
vList = m_ViewList;//防止m_ViewList元素增加导致内存地址改变
changeFlag = true;
}
}
}


//发送新加入可视列表的对象(有视野变化并且当前实体是玩家才发送)
if (changeFlag && m_btType == ePlayer)
{
ViewStruct *vList = m_ViewList;
for (INT_PTR i = m_ViewList.count()-1; i > -1; --i)
{
if (vList[i].mask == VIEW_STATE_ENTER)
{
vList[i].mask = VIEW_STATE_STAY;//停留状态(表示处理消息)
sendMsg(PM_ACTOR_APPEAR, 0, (INT_PTR)vList[i].pEntity, 0, 0, 0);//发送玩家视野实体的进入消息(会在角色循环里处理该角色消息系统消息)
}
}
}
}


//填充当前块中可视范围内所有块的对象到实体列表
int CMapGrid::getEntityList(int left, int right, int top, int bottom, CBList<CEntity*>& EList, CEntity* pEntity)
{
//最小取0
left = (left > 0) ? left : 0;
top = (top > 0) ? top : 0;
//最大不超过边界
right = (right >= (int)m_nGridWidth) ? m_nGridWidth-1 : right;
bottom = (bottom >= (int)m_nGridHeight) ? m_nGridHeight-1 : bottom;

EList.clear();
int nCurGrid = -1;
for (int y = top; y <= bottom; ++y)
{
for (int x = left; x <= right; ++x)
{
nCurGrid = y*m_nGridWidth + x;
CEntity** ppList = m_pGrid[nCurGrid];//获取该网格的实体的列表(地图)
for (INT_PTR i = m_pGrid[nCurGrid].count()-1; i > -1; --i)
{
if (pEntity != ppList[i])//不是自己
{
EList.add(ppList[i]);//添加到实体对象列表
}
}
}
}
return (INT)EList.count();
}


其中:m_pGrid 是地图网格实体列表数组,容纳的是地图范围内的所有的实体,单位是网格。

以下是其初始化。

bool CMapGrid::init(int pxWidth, int pxHeight)
{
m_nGridWidth = ((pxWidth + 127) & (~127)) / 128;
m_nGridHeight = ((pxHeight + 127) & (~127)) / 128;

m_pGrid = new MapGridCell[m_nGridWidth*m_nGridHeight];//申请m_maxGrid_x*m_maxGrid_y块内存
return true;
}


(3)如果角色死亡或者下线则会从地图的网格实体列表里移除

角色消失

void CEntity::disappear()
{
if (!m_boDisappeared)
{
setScene(NULL, m_nX, m_nY);
m_boDisappeared = true;
m_dwDisappearTick = g_LogicEngine->getTickCount();
}
}

角色从场景的地图的网格的实体列表中移除

void CEntity::setScene(Scene* scene,int x,int y)
{
if (scene != m_pScene)
{
 
Scene *oldScene = m_pScene;
int ox = m_nX, oy = m_nY;


if (m_pScene)
{
m_pScene->m_pMapGrid->removeFromGrid(m_nX, m_nY, this);
}
m_pScene = scene;
m_nX = x;
m_nY = y;
if (m_pScene)
{
m_pScene->m_pMapGrid->addToGrid(m_nX, m_nY, this);
}
sceneChanged(oldScene, ox, oy);
}
else setXY(x, y);
}



0 0
原创粉丝点击