文档中心
ExpressVideoSDK 实时音视频
文档中心
体验 App
SDK 中心
API 中心
常见问题
代码市场
进入控制台
立即注册
登录
中文站 English
  • 文档中心
  • 实时音视频
  • 通信能力
  • 多人状态实时同步

多人状态实时同步

更新时间:2024-09-27 21:38

功能简介

ZEGO Express SDK 从 3.0.0 版本起,新增多人状态实时同步功能,提供有序、高频、低延时、大规模的状态同步服务,帮助开发者快速实现虚拟玩法中玩家的位置、动作、形象等实时信息同步能力,同时单场景中支持 1 万个用户同时在线。

在大型虚拟世界中,用户一般不需要感知较远的场景或远端用户,ZEGO 提供 AOI(Area Of Interest)能力,用于减免获取用户可见范围外的信息,极大减少客户流量成本、用户端流量及性能消耗。

概念解释

  • 场景:用户需要先登录场景,进入同一个场景的用户才可以进行状态信息的同步。
  • AOI(Area of interest)范围:用户感兴趣区域的正方形大小,在虚拟场景中一般为用户的可见范围,该范围实时跟随用户的位置,仅会同步 AOI 范围内远端的用户信息。

应用场景

  • 虚拟办公、虚拟会展、虚拟社交、虚拟 KTV 等元宇宙场景。
  • 需要超高频、低延时、大规模同步信息或控制指令的通用场景。

前提条件

使用该服务会产生相应的费用,请联系 ZEGO 商务人员了解具体的费用情况。

在实现状态同步前,请确保:

  • 已联系 ZEGO 技术支持进行特殊编包并开通状态同步服务。
  • 已在项目中集成 ZEGO Express SDK。
  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理。

实现流程

1 创建引擎

调用 createEngine 接口,将申请到到 AppID 和 AppSign 传入参数 “appID” 和 “appSign”,创建引擎单例对象。引擎当前只支持同时创建一个实例,超出后将返回 null。

/** 定义 SDK 引擎对象 */
ZegoExpressEngine engine;

ZegoEngineProfile profile = new ZegoEngineProfile();
/** 请通过官网注册获取,格式为 123456789L */
profile.appID = appID;
/** 64个字符,请通过官网注册获取,格式为"0123456789012345678901234567890123456789012345678901234567890123" */
profile.appSign = appSign;
/** 通用场景接入 */
profile.scenario = ZegoScenario.DEFAULT;
/** 设置app的application 对象 */
profile.application = getApplication();
/** 创建引擎 */
engine = ZegoExpressEngine.createEngine(profile, null);

2 创建范围场景模块

调用 createRangeScene 接口创建范围场景实例,当前只支持同时创建一个实例,超出后将返回 null。

/** 定义范围场景对象 */
ZegoRangeScene rangeScene;

/** 创建范围场景 */
rangeScene = engine.createRangeScene();

3 监听范围场景事件回调

根据需要调用 ZegoRangeScene.setEventHandler 接口设置范围场景,用于监听范围场景状态、登录状态、进出 AOI 通知等。调用 ZegoRangeSceneItem.setEventHandler 接口设置范围场景物品管理类事件回调,用于监听物品进入离开 AOI 范围、物品绑定状态变更、物品状态和命令更新事件。

/** 设置范围场景事件回调 */
rangeScene.setEventHandler(new IZegoRangeSceneEventHandler() {
    @Override
    public void onSceneStateUpdate(ZegoRangeScene rangeScene, ZegoSceneState state, int errorCode) {
        super.onSceneStateUpdate(rangeScene, state, errorCode);
    }

    @Override
    public void onEnterView(ZegoRangeScene rangeScene, ZegoUser user, ZegoPosition position) {
        super.onEnterView(rangeScene, user, position);
    }

    @Override
    public void onLeaveView(ZegoRangeScene rangeScene, String userID) {
        super.onLeaveView(rangeScene, userID);
    }

    @Override
    public void onUserStatusUpdate(ZegoRangeScene rangeScene, String userID, ZegoPosition position, int channel, byte[] status) {
        super.onUserStatusUpdate(rangeScene, userID, position, channel, status);
    }

    @Override
    public void onUserCommandUpdate(ZegoRangeScene rangeScene, String userID, ZegoPosition position, int channel, byte[] command) {
        super.onUserCommandUpdate(rangeScene, userID, position, channel, command);
    }

    @Override
    public void onCustomCommandUpdate(ZegoRangeScene rangeScene, byte[] command) {
        super.onCustomCommandUpdate(rangeScene, command);
    }
});

/** 设置范围场景物品管理类事件回调 */
rangeScene.getRangeSceneItem().setEventHandler(new IZegoRangeSceneItemEventHandler() {
    @Override
    public void onItemEnterView(ZegoRangeScene rangeScene, long itemID, int capacity, ZegoPosition position, ArrayList<String> userList) {
        super.onItemEnterView(rangeScene, itemID, capacity, position, userList);
    }

    @Override
    public void onItemLeaveView(ZegoRangeScene rangeScene, long itemID) {
        super.onItemLeaveView(rangeScene, itemID);
    }

    @Override
    public void onItemBindUpdate(ZegoRangeScene rangeScene, long itemID, String userID) {
        super.onItemBindUpdate(rangeScene, itemID, userID);
    }

    @Override
    public void onItemUnbindUpdate(ZegoRangeScene rangeScene, long itemID, ArrayList<String> userList) {
        super.onItemUnbindUpdate(rangeScene, itemID, userList);
    }

    @Override
    public void onItemStatusUpdate(ZegoRangeScene rangeScene, long itemID, ZegoPosition position, int channel, byte[] status) {
        super.onItemStatusUpdate(rangeScene, itemID, position, channel, status);
    }

    @Override
    public void onItemCommandUpdate(ZegoRangeScene rangeScene, long itemID, ZegoPosition position, int channel, byte[] command) {
        super.onItemCommandUpdate(rangeScene, itemID, position, channel, command);
    }
});

4 登录场景

调用 loginScene 接口,传入场景参数:sceneID、user、position、broadcastMode,即可登录场景。

  • 同一个 AppID 内,需保证 userID 全局唯一,建议开发者将其设置成一个有意义的值,可将 userID 与自己业务账号系统进行关联。
  • userID 不能为空,否则会导致登录场景失败。
/** 登录场景参数 */
ZegoSceneParam param = new ZegoSceneParam();
/** 创建用户 */
ZegoUser user = new ZegoUser(userID, userName);
/** 设置用户的场景坐标、运动朝向、摄像机朝向 */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
    position.cameraOrientation.axisForward[i] = rotateMatrixFront[i];
    position.cameraOrientation.axisRight[i] = rotateMatrixRight[i];
    position.cameraOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** 设置场景 ID */
param.sceneID = sceneID;
/** (可选)配置模版 ID */
param.templateID = template_id;
param.user = user;
param.position = position;
/** 设置用户登录场景的广播模式 */
param.broadcastMode = ZegoBroadcastMode.ALL;
rangeScene.loginScene(param, new IZegoRangeSceneLoginSceneCallback() {
    @Override
    public void onLoginSceneCallback(int errorCode, ZegoSceneConfig config) {
    }
});

如果您需要自定义模板,详情请参考 服务端 API - 场景模版配置。

5 同步状态

通过 updateUserStatus、updateUserCommand 接口更新用户状态及用户命令。通过 onUserStatusUpdate、onUserCommandUpdate 回调接收 AOI 范围内的远端用户位置、命令等状态信息。

  • 用户状态(status,S)是全量更新,用户命令是增量更新(command,C),用户状态可以由前一个用户状态经过一系列用户命令计算得出。
  • 用户更新状态的模型应是 Si->Ci->Ci+1->Ci+2->...Ci+j->S(i+1)->C(i+1)+1->C(i+1)+2->...C(i+1)+k->S(i+2)->C(i+2)->...
  • 状态同步服务将保证 Si、Si+1 两个全量状态(status)之间的增量更新(command)有序到达。
/** 设置用户的场景坐标、运动朝向、摄像机朝向 */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
    position.cameraOrientation.axisForward[i] = rotateMatrixFront[i];
    position.cameraOrientation.axisRight[i] = rotateMatrixRight[i];
    position.cameraOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** 更新用户状态 */
byte[] status = new byte[10];
int errorCode = rangeScene.updateUserStatus(position, 0, status);

/** 更新用户命令 */
byte[] command = new byte[10];
int errorCode = rangeScene.updateUserCommand(position, 0, command);

6 (可选)获取场景内用户数、获取 AOI 范围内用户列表

获取场景内用户数、获取 AOI 范围内用户列表

可以根据需要调用 getUserCount、getUserListInView 接口获取场景内用户数、AOI 范围内用户列表。

/** 获取场景内用户数 */
rangeScene.getUserCount(new IZegoRangeSceneGetUserCountCallback() {
    @Override
    public void onGetUserCountCallback(int errorCode, int count) {
    }
});

/** 获取 AOI 范围内用户列表 */
rangeScene.getUserListInView(new IZegoRangeSceneGetUserListInViewCallback() {
    @Override
    public void onGetUserListInViewCallback(int errorCode, ArrayList<String> userList) {
    }
});

7(可选)物品状态与竞争锁

物品状态与竞争锁

通过物品状态同步可以实现虚拟场景中物品移动及放置玩法,如放置系统、踢球等。通过物品锁可以实现物品抢夺玩法,如捡装备、抢椅子等,可根据需求进行设置。

  1. 创建物品

根据需要调用 createItem 接口在场景内创建物品。

/** 创建物品参数 */
ZegoItemParam param = new ZegoItemParam();
/** 设置物品的场景坐标、运动朝向 */
ZegoPosition position = new ZegoPosition();
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** 设置物品 ID */
param.itemID = itemID;
/** 设置物品最大绑定人数上限 */
param.capacity = capacity;
param.position = position;
/** 设置创建物品模式 */
param.createMode = ZegoCreateItemMode.NO_BIND;
rangeScene.getRangeSceneItem().createItem(param, new IZegoRangeSceneCreateItemCallback() {
    @Override
    public void onCreateItemCallback(int errorCode, long itemID) {
    }
});
  1. 绑定物品

如果需要对物品进行操作(即更新物品状态命令),首先需要调用 bindItem 绑定物品。

rangeScene.getRangeSceneItem().bindItem(itemID, new IZegoRangeSceneBindItemCallback() {
    @Override
    public void onBindItemCallback(int errorCode, long itemID) {
    }
});
  1. 同步物品状态

通过 updateItemStatus、updateItemCommand 接口更新物品状态及物品命令。通过 onItemStatusUpdate、onItemCommandUpdate 回调接收 AOI 范围内的远端物品位置、命令等状态信息。

  • 在更新物品状态和命令前,需要先绑定物品。
  • 物品状态(status,S)是全量更新,物品命令是增量更新(command,C),物品状态可以由前一个物品状态经过一系列物品命令计算得出。
  • 物品更新状态的模型应是 Si->Ci->Ci+1->Ci+2->...Ci+j->S(i+1)->C(i+1)+1->C(i+1)+2->...C(i+1)+k->S(i+2)->C(i+2)->...
  • 状态同步服务将保证 Si、Si+1 两个全量状态(status)之间的增量更新(command)有序到达。
/** 设置物品的场景坐标、运动朝向 */
ZegoPosition position = new ZegoPosition()
for (int i = 0; i < 3; ++i) {
    position.coordinate[i] = coordinate[i];
    position.motionOrientation.axisForward[i] = rotateMatrixFront[i];
    position.motionOrientation.axisRight[i] = rotateMatrixRight[i];
    position.motionOrientation.axisUp[i] = rotateMatrixUp[i];
}
/** 更新物品状态 */
byte[] status = new byte[10];
rangeScene.getRangeSceneItem().updateItemStatus(itemID, position, 0, status, new IZegoRangeSceneUpdateItemStatusCallback() {
    @Override
    public void onUpdateItemStatusCallback(int errorCode, long itemID) {
    }
});

/** 更新物品命令 */
byte[] command = new byte[10];
rangeScene.getRangeSceneItem().updateItemCommand(itemID, position, 0, command, new IZegoRangeSceneUpdateItemCommandCallback() {
    @Override
    public void onUpdateItemCommandCallback(int errorCode, long itemID) {
    }
});
  1. 解绑物品

如果无需对物品进行操作(即更新物品状态命令),可以调用 unbindItem 解绑物品。

rangeScene.getRangeSceneItem().unbindItem(itemID, new IZegoRangeSceneUnbindItemCallback() {
    @Override
    public void onUnbindItemCallback(int errorCode, long itemID) {
    }
});
  1. 销毁物品

根据需要调用 destroyItem 销毁场景内的物品。

rangeScene.getRangeSceneItem().destroyItem(itemID, new IZegoRangeSceneDestroyItemCallback() {
    @Override
    public void onDestroyItemCallback(int errorCode, long itemID) {
    }
});

8 退出场景

调用 logoutScene 接口退出场景。

/** 退出场景 */
rangeScene.logoutScene(new IZegoRangeSceneLogoutSceneCallback() {
    @Override
    public void onLogoutSceneCallback(int errorCode) {
    }
});

9 销毁范围场景模块

当不再使用范围场景模块时,可以调用 destroyRangeScene 接口销毁范围场景模块。

engine.destroyRangeScene(rangeScene);

10 销毁引擎

当不再使用 ZEGO Express SDK 时,可以调用 destroyEngine 销毁引擎。

ZegoExpressEngine.destroyEngine(null);
本篇目录
  • 免费试用
  • 提交工单
    咨询集成、功能及报价等问题
    电话咨询
    400 1006 604
    咨询客服
    微信扫码,24h在线

    联系我们

  • 文档反馈