You need to enable JavaScript to run this app.
导航

场景搭建(Android)

最近更新时间2023.12.13 16:13:04

首次发布时间2022.11.15 15:26:04

SDK集成

本场景需要集成火山引擎的RTC SDK 以及SUD MGP 互动小游戏接入平台,您需要在 RTC 的控制台开通服务,并下载对应的SUD MGP 的SDK,相应开通指南如下:

RTC SDK 接入

详细细节请参见 RTC服务开通指南

alt

小游戏 SDK 接入

整体实现流程

alt


核心功能实现

房主创建游戏房以及观众加入游戏房流程:

时序图

alt

示例代码

/**
     * 加入RTC房间并初始化参数 
     * @param token 加入 RTC 房间的 token
     * @param roomId 加入 RTC 房间的 id
     * @param userId 加入 RTC 房间的 用户id
     */
    public static void joinRoom(String token, String roomId, String uid) {
        MLog.d("joinChannel", "token:" + token + " roomId:" + roomId + " uid:" + uid);
        leaveRoom();
        if (mRTCVideo == null) {
            return;
        }
        mRTCRoom = mRTCVideo.createRTCRoom(roomId);
        mRTCRoom.setRTCRoomEventHandler(mRTCRoomEventHandler);
        UserInfo userInfo = new UserInfo(uid, null);
        RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_COMMUNICATION,
                true, true, true);
        mRTCRoom.joinRoom(token, userInfo, roomConfig);
}
    /**
     * 初始化SudMGP sdk
     *
     * @param activity 游戏所在页面
     * @param gameId   游戏id
     * @param code     令牌
     */
    private void initSdk(FragmentActivity activity, long gameId, String code) {
        String appId = getAppId();
        String appKey = getAppKey();
        // 初始化sdk
        SudMGP.initSDK(activity, appId, appKey, isTestEnv(), new ISudListenerInitSDK() {
            @Override
            public void onSuccess() {
                loadGame(activity, code, gameId);
            }

            @Override
            public void onFailure(int errCode, String errMsg) {
                if (isTestEnv()) {
                    Toast.makeText(activity, "initSDK onFailure:" + errMsg + "(" + errCode + ")", Toast.LENGTH_LONG).show();
                }

                delayLoadGame(activity, gameId);
            }
        });
    }

    /**
     * 加载游戏
     * APP和游戏的相互调用
     * ISudFSTAPP:APP调用游戏的接口
     * ISudFSMMG:游戏调APP的响应回调
     *
     * @param activity 游戏所在页面
     * @param code     登录令牌
     * @param gameId   游戏id
     */
    private void loadGame(FragmentActivity activity, String code, long gameId) {
        if (activity.isDestroyed() || !isRunning || gameId != playingGameId) {
            return;
        }

        // 给装饰类设置回调
        sudFSMMGDecorator.setSudFSMMGListener(this);

        // 调用游戏sdk加载游戏
        ISudFSTAPP iSudFSTAPP = SudMGP.loadMG(activity, getUserId(), gameRoomId, code, gameId, getLanguageCode(), sudFSMMGDecorator);

        // 如果返回空,则代表参数问题或者非主线程
        if (iSudFSTAPP == null) {
            Toast.makeText(activity, "loadMG params error", Toast.LENGTH_LONG).show();
            delayLoadGame(activity, gameId);
            return;
        }

        // APP调用游戏接口的装饰类设置
        sudFSTAPPDecorator.setISudFSTAPP(iSudFSTAPP);

        // 获取游戏视图,将其抛回Activity进行展示
        // Activity调用:gameContainer.addView(view, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        gameView = iSudFSTAPP.getGameView();
        onAddGameView(gameView);
    }
    
    /**
     * 处理游戏视图信息(游戏安全区)
     * 文档:https://docs.sud.tech/zh-CN/app/Client/API/ISudFSMMG/onGetGameViewInfo.html
     */
    public void processOnGetGameViewInfo(View gameView, ISudFSMStateHandle handle) {
        //拿到游戏View的宽高
        int gameViewWidth = gameView.getMeasuredWidth();
        int gameViewHeight = gameView.getMeasuredHeight();
        if (gameViewWidth > 0 && gameViewHeight > 0) {
            notifyGameViewInfo(handle, gameViewWidth, gameViewHeight);
            return;
        }

        //如果游戏View未加载完成,则监听加载完成时回调
        gameView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                gameView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int width = gameView.getMeasuredWidth();
                int height = gameView.getMeasuredHeight();
                notifyGameViewInfo(handle, width, height);
            }
        });
    }

    /** 通知游戏,游戏视图信息 */
    private void notifyGameViewInfo(ISudFSMStateHandle handle, int gameViewWidth, int gameViewHeight) {
        GameViewInfoModel gameViewInfoModel = new GameViewInfoModel();
        gameViewInfoModel.ret_code = 0;
        // 游戏View大小
        gameViewInfoModel.view_size.width = gameViewWidth;
        gameViewInfoModel.view_size.height = gameViewHeight;

        // 游戏安全操作区域
        getGameRect(gameViewInfoModel);

        // 给游戏侧进行返回
        String json = SudJsonUtils.toJson(gameViewInfoModel);
        handle.success(json);
    }

观众请求上麦和房主拉观众上麦

观众请求上麦时序图

alt

房主拉观众上麦时序图

alt

示例代码

/**
 * 观众举手
 */
public void onCSRaiseHandsMicEvent(RaiseHandsMicEvent event) {
    mChatRoomAdapter.updateRaiseHandStatus(event.userId);
    mIsSomeoneRaiseHand = true;
    updateUI();
}

/**
 * 主动邀请上麦
 */
public void onCSInviteMicEvent(InviteMicEvent event) {
    if (!GameRoomDataManger.isSelf(event.userId)) {
        return;
    }
    if (mDialog != null) {
        mDialog.dismiss();
    }
    if (TextUtils.equals(event.userId, mUserId)) {
        CommonDialog dialog = new CommonDialog(this);
        dialog.setMessage("主播邀请您上麦");
        dialog.setPositiveListener(v -> {
            GameRoomRtmClient rtmClient = GameRoomRtcManager.getRtmClient();
            if (rtmClient != null) {
                rtmClient.confirmBecomeSpeaker();
            }
            dialog.dismiss();
        });
        dialog.setNegativeListener(v -> dialog.dismiss());
        dialog.show();
        mDialog = dialog;
    }
}

/**
 * 收到上麦邀请
 */
public void onCSInviteMicEvent(InviteMicEvent event) {
    if (!GameRoomDataManger.isSelf(event.userId)) {
        return;
    }
    if (mDialog != null) {
        mDialog.dismiss();
    }
    if (TextUtils.equals(event.userId, mUserId)) {
        CommonDialog dialog = new CommonDialog(this);
        dialog.setMessage("主播邀请您上麦");
        dialog.setPositiveListener(v -> {
            GameRoomRtmClient rtmClient = GameRoomRtcManager.getRtmClient();
            if (rtmClient != null) {
                rtmClient.confirmBecomeSpeaker();
            }
            dialog.dismiss();
        });
        dialog.setNegativeListener(v -> dialog.dismiss());
        dialog.show();
        mDialog = dialog;
    }
}

/**
 * 成功上麦通知
 */
public void onCSMicOnEvent(MicOnEvent event) {
    if (TextUtils.equals(event.user.userId, mUserId)) {
        if (!hasAudioPermission()) {
            SafeToast.show("麦克风权限已关闭,请至设备设置页开启");
            requestPermissions(Manifest.permission.RECORD_AUDIO);
        }
        showToast("您已经成功上麦", true);
        mIsSpeaker = true;
        mIsRaiseHand = false;
        switchMic(true);
    }
    mChatRoomAdapter.onUserRoleChange(event.user.userId, true);
    updateUI();
}

/**
 * 下麦通知
 */
public void onCSMicOffEvent(MicOffEvent event) {
    if (TextUtils.equals(event.userId, mUserId)) {
        showToast("您已回到听众席", true);
        mIsSpeaker = false;
        switchMic(false);
    }
    mChatRoomAdapter.onUserRoleChange(event.userId, false);
    updateUI();
}

核心功能 API 与回调参考

API

功能点API
创建 RTCVideo 对象createRTCVideo
创建 RTCRoom 对象createRTCRoom
设置用户可见性setUserVisibility
开启本地音频采集startAudioCapture
加入RTC房间joinRoom
离开房间leaveRoom
关闭内部音频采集stopAudioCapture
销毁 RTCRoom 对象destroy

回调

功能点回调
本地用户加入 RTC 房间回调onRoomStateChanged
远端可见用户加入房间onUserJoined