我正在开发一款多人Flash游戏。服务器通知每个客户端其他玩家在玩家附近。为此,服务器必须连续检查哪些客户端彼此靠近。以下是我目前使用的,作为临时解决方案:
private function checkVisibilities()
{
foreach ($this->socketClients as $socketClient1)
{ //loop every socket client
if (($socketClient1->loggedIn()) && ($socketClient1->inWorld()))
{ //if this client is logged in and in the world
foreach ($this->socketClients as $cid2 => $socketClient2)
{ //loop every client for this client to see if they are near
if ($socketClient1 != $socketClient2)
{ //if it is not the same client
if (($socketClient2->loggedIn()) && ($socketClient2->inWorld())
{ //if this client is also logged in and also in the world
if ((abs($socketClient1->getCharX() - $socketClient2->getCharX()) + abs($socketClient1->getCharY() - $socketClient2->getCharY())) < Settings::$visibilities_range)
{ //the clients are near each other
if (!$socketClient1->isVisible($cid2))
{ //not yet visible -> add
$socketClient1->addVisible($cid2);
}
}
else
{ //the clients are not near each other
if ($socketClient1->isVisible($cid2))
{ //still visible -> remove
$socketClient1->removeVisible($cid2);
}
}
}
else
{ //the client is not logged in
if ($socketClient1->isVisible($cid2))
{ //still visible -> remove
$socketClient1->removeVisible($cid2);
}
}
}
}
}
}
工作正常。然而,到目前为止,我一直只玩2名球员。此函数为每个客户端循环每个客户端。因此,每次运行该功能时,100个玩家将是100 * 100 = 10.000个循环。这似乎不是最好或最有效的方法。
现在我想知道你们对我目前的设置有什么看法,以及你是否有更好的方法来处理这些可见性。
更新:我忘了提到世界是无限的。它实际上是“宇宙”。没有地图。此外,它是一个二维(2D)游戏。
提前致谢。
答案 0 :(得分:4)
我要说的第一件事是你的代码从里到外看。为什么你有一个高级游戏逻辑功能,必须做的工作是检查哪些客户端登录和在世界上?应该从游戏逻辑中删除所有网络内容,以便它在更高级别上完成,并且游戏内逻辑只需要处理当前正在玩的和世界上的玩家。这给你留下了一个简单的问题:这两个玩家是否足够接近?这里只需要一个简单的距离检查就足够了。
接下来就是减少你做的循环量。距离通常是一个可交换的属性,因此您不需要检查A和B之间以及B和A之间的距离。为此,当您的第一个循环遍历所有客户端时,第二个循环只需要迭代所有在第一个之后来的客户。这会使您需要执行的迭代次数减少一半。
你也不必像你所说的那样不断地这样做。你必须经常这样做,以确保游戏顺利运行。如果移动速度不是那么高,那么你可能每隔几秒钟就必须这样做才能足够好。
如果这对你来说仍然不够好,那么ianh所描述的某种空间散列系统是减少你所做查询次数的好方法。网格是最简单的,但某种树结构(理想情况下是自平衡)是另一种选择。
答案 1 :(得分:3)
最直接的解决方案是将世界划分为统一网格,如下所示:
_|____|____|____|_
| | | |
_|____|____|____|_
| | | |
_|____|____|____|_
| | | |
_|____|____|____|_
| | | |
然后将对象插入到它们相交的任何网格图块中:
_|____|____|____|_
| @ | | |
_|____|____|____|_
| |d d | |
_|____|____|____|_
| | d | d |
_|____|____|____|_
| | | |
现在要对附近的对象进行查询,您只需要查看附近的单元格。例如,要查看来自播放器(@
)的一个磁贴中的哪个人,您只需要签入9个磁贴,而不是整个地图:
/|////|////|____|_ /|/@//|////| | /|////|////|____|_ /|////|d/d/| | /|////|////|____|_ | | d | d | _|____|____|____|_ | | | |
然而,根据您的世界,这种技术可能非常浪费:可能会有很多空单元格。如果这成为问题,您可能希望实现更复杂的spatial index。
答案 2 :(得分:2)
尝试使用四叉树来表示玩家的位置 维基文章为here 它的作用是将您在空间(用户)中提供的对象保存在树中,该树根据需要对空间(平面)进行分区。 至于无限问题 - 编程中没有任何东西真的是无限的,所以定义一个不能被用户传递的边界(即使是一个非常大的数字用于坐标,这将需要用户100年左右才能到达)