高级使用MMORoom和MMO API
本文要求您已经熟悉SmartFoxServer 2X 2.8版中引入的MMORoom功能。这里我们介绍一些可以从服务器端使用的高级技术。如果您不熟悉MMORooms和MMO API,请务必阅读MMORoom概述。
自定义感兴趣区域(AoI)
服务器端MMO API的一个高级方面是使用与MMORoom中配置的AoI小于或等于的自定义AoI的能力。该功能允许限制广播特定事件的地图区域。
我们来看看这个简单的2D地图:
绿色圆圈是我们的球员,橙色区域代表AoI,所以玩家产生的所有事件将被传送到落在该接近范围内的其他客户端(橙色圆圈)。从扩展代码中,我们还可以广播影响较小区域的事件和消息:在这种情况下,青色矩形表示可以在运行时定义的较小的AoI。
“ 有可能的使用
自定义AoI的应用可以是不同的,例如仅向较短范围内的用户发送公共消息,或者触发主AoI内的区域中的事件,例如。一场爆炸只会影响更接近事件来源的玩家。
服务器端SFSMMOApi类(在com.smartfoxserver.v2.api包下)暴露了一些有用的方法来执行此操作:
sendPublicMessage(Room room,User sender,String message,ISFSObject params,Vec3D aoi)
sendObjectMessage(Room room,User sender,ISFSObject message,Vec3D aoi)
收件人列表作为“常规API”而不是在发件人周围提供代表AoI的Vec3D。正如我们已经提到的那样,自定义的AoI必须是<= default AoI。
问:为什么我们不能定义任何大小的自定义AoI,例如更大的房间的默认AoI
MMORoom使用高度优化的内部数据结构来保持所有查找操作非常快速和高效,即使数以千计的用户和MMOItems同时进行交互。为了做到这一点,数据结构使用默认的AoI作为主要的存储单元,并且只允许在这些单元内进行搜索。
如果您需要根据MMORoom的多个区域选择一组更复杂的用户,那么您仍然可以手动进行操作,我们将在下几段中详细介绍。
»扩展MMOItem类
我们在MMORoom概述文章中看到,MMOItem可以用于表示各种交互对象:触发器,奖金,投射等…我们现在将通过定义两个特定实体来显示一个实际示例,我们将在假设游戏:开关和门。
我们希望我们的MMORoom拥有一些秘密通道,可以通过位于地图的几个开关在远处打开。当开关被激活时,附近的秘密门将打开,我们将触发此事件给客户显示门开启(或关闭)的动画。
为了在服务器端描述这些对象,我们将扩展MMOItem类并添加所需的额外信息位:
public class SwitchItem extends MMOItem
{
private boolean state;
public DoorItem(boolean state)
{
super();
this.state = state;
}
public boolean getState()
{
return state;
}
}
public class DoorItem extends MMOItem
{
private Vec3D location;
private SwitchItem relatedSwitch;
public DoorItem(Vec3D location, SwitchItem relatedSwitch)
{
super();
this.location = location;
this.relatedSwitch = relatedSwitch;
}
public Vec3D getLocation()
{
return location;
}
public boolean isOpen()
{
return relatedSwitch.getState();
}
}
我们的SwitchItem已经添加了一个表示其状态的额外的布尔值,而DoorItem有一个Vec3D表示其在地图上的位置,并引用了打开/关闭通道的相关SwitchItem。
»扩展信息
现在我们已经定义了两个游戏项目,让我们看看我们如何才能从扩展代码触发一个事件,只有那些落入门的AoI的用户。
public void handleSecretDoorStateChange(DoorItem secretPassage)
{
SFSObject msg = new SFSObject();
// we add the Id of the passage to be opened/closed我们添加要打开/关闭的通道的Id
msg.putInt("itemId", secretPassage.getId());
// we add the status of the secret passage我们添加秘密通道的状态
msg.putBool("open", secretPassage.isOpen());
// Obtain the list of Users within the item's AOI获取项目AOI中的用户列表
List<User> recipients = getParentRoom().getProximityList(secretPassage.getLocation());
// Send event to selected clients将事件发送到所选客户端
send("handleDoor", msg, recipients);
}
这里的“诀窍”是使用MMORoom的方法来获得我们需要的。在这种情况下,我们会询问一些属于默认AoI的用户的列表。 MMORoom对象为这些任务提供了几种有用的方法:
public List <User> getProximityList(用户目标)
在所提供的目标客户端周围获取默认AoI中的用户列表
public List <User> getProximityList(用户目标,Vec3D aoi)
在提供的目标客户端周围获取自定义AoI中的用户列表
public List <User> getProximityList(Vec3D position)
在所提供的目标坐标周围获取默认AoI内的用户列表
public List <User> getProximityList(Vec3D position,Vec3D aoi)
在提供的目标坐标周围获取自定义AoI中的用户列表
现在我们假设我们的游戏也支持地板门。发现隐藏的开关到陷阱门的玩家可以等待一个或多个不必要的用户在正确的位置移动,然后激活门来捕获它们。
在这种情况下,事件只应传播到房间较小区域的用户:只有位于陷阱门周边的用户。我们可以通过使用自定义AoI并调用上一个列表中的最后一个方法来完成此任务。
»高级技巧
如前所述,我们可以通过使用自定义的AoI来执行房间内的搜索,只要它的大小小于或等于默认的AoI。这应该涵盖95%的情况,但剩下的几个情况呢?
为了这个例子,我们将有一个播放器生成一个应该被默认AoI之外的更多的用户收到的事件。让我们假设我们的RPG英雄刚刚施放地震,其效果必须传播到比默认AoI大得多的区域。
为了完成这个任务,我们可以简单地在相邻区域(AoI)中执行多个用户查找,将结果聚合到一个列表中,最后与通常的send(…)扩展方法一起使用。换句话说,我们可以使用默认AoI作为虚拟地图的细分单位,并通过将目标坐标移动一个或多个单位来执行多次搜索。
默认情况下,MMORoom只能在1个单位的范围内进行搜索,但是从服务器代码中我们可以自由地聚合多个搜索,甚至可以从整个MMORoom的用户列表中选择特定的玩家,从而允许高度灵活地过滤特定的收件人服务器消息或事件。
翻译自http://docs2x.smartfoxserver.com/AdvancedTopics/advanced-mmo-api