unityController - Enter フレーム

Enter フレームセクションには、各フレームまたは同様の方法でトリガされるメソッドが含まれています。

on_new_frame

on_new_frame(frameSource: genvid.IDataFrame)

on_new_frame メソッドは、常に更新が必要な様々なタスクを実行します。作成した Genvid クライアント用に、フレームごとに呼び出します。

  1. まずアノテーションデータを取得します。有効かどうかを確認し、その後コンソールログを表示します。

  2. さまざまな処理を行う前に、ゲームデータを取得してから、そのデータが有効であることを確認する必要があります。

  3. データの妥当性が確認できたら、データ (キューブ) からオブジェクトリストを取得します。

  4. 次のカメラとシーンまでの残り時間のテキストフィールドを更新します。

  5. プレイヤーテーブルをセットアップしたかどうかを確認します。設定されていない場合、 initPlayerTable を呼び出します。

  6. オブジェクト配列を使用して、WebGL で輪を作るループ処理を作成します。

  7. 同じループで、各オブジェクトの popularity 値と位置を更新します。

  8. ループ処理の終了後、WebGL の輪を描画します。

                // Parse the JSON from each elements
                let gameDataFrame = frameSource.streams["GameData"];
                let gameData: IGameData = null;
    
                // Log the annotations
                const colorChangedAnnotation = frameSource.annotations["ColorChanged"];
                if (colorChangedAnnotation) {
                    for (let annotation of colorChangedAnnotation) {
                        const dataColor: IGameChangeColorAnnotationList  = annotation.user;
                        for (let colorChangeElement of dataColor.colorList) {
                            console.info(`The cube ${colorChangeElement.name} changed to color ${colorChangeElement.color}`);
                        }
                    }
                }
    
                if (gameDataFrame && gameDataFrame.user) {
    
                    gameData = gameDataFrame.user;
                    this.lastGameData = gameData;
                    let cubes = gameData.cubes;
    
                    // Let update the time of camera and scene
                    let textForTime = "Time until next camera: " + gameData.camera.toFixed(2) + " seconds \r\n Time until next scene: " + gameData.scene.toFixed(2) + " seconds";
                    this.timeCamSceneDiv.textContent = textForTime;
    
                    // Setup the player table once the game data is ready
                    if (this.playerTableSetupCompletion === false) {
                        this.playerTableSetupCompletion = true;
                        let cubes = gameData.cubes;
    
                        this.initPlayerTable(cubes);
                    }
    
                    // Perform the webgl update process -- Update 3d
                    if (cubes) {
                        let vertices: number[] = [];
                        for (let cube of cubes) {
                            let m = cube.mat;
                            let p = genvidMath.vec3(m.e03, m.e13, m.e23);
                            let r = this.circleRadius;
                            let c = genvidMath.vec4(1, 0, 0, 1);
                            if (cube.color) {
                                c = genvidMath.vec4(cube.color.r, cube.color.g, cube.color.b, cube.color.a);
                            }
                            if (cube.selected && this.selection.length === 0) {
                                // To gather initial state.
                                this.setSelection(cube.name);
                            }
                            if (!this.isSelected(cube.name)) {
                                c = genvidMath.muls4D(c, 0.5);
                            }
                            this.makeCircleZ(vertices, p.x, p.y, p.z, r, c);
    
                            // Move the name tag of the cube
                            let tag = this.findOrCreateTagDiv(cube);
                            let mat = this.convertMatrix(gameData.MatProjView);
                            let pos_2d = genvidMath.projectPosition(mat, p);
                            this.center_at(tag, pos_2d, genvidMath.vec2(0, -75));
    
                            // Modify the popularity with the latest value
                            if (this.latestPopularity) {
                                let pCube = this.latestPopularity.cubes.find((c) => { return c.name === cube.name; });
                                if (pCube) {
                                    cube.popularity = pCube.popularity;
                                }
                            }
    
                            cube.popText = this.popularityToText(cube.popularity);
                            let cubePopSpan = <HTMLSpanElement>document.querySelector(".cube" + cube.name + "_cheer");
                            cubePopSpan.textContent = cube.popText;
    
                            let cubePosXSpan = <HTMLSpanElement>document.querySelector("#cube" + cube.name + "_position_x");
                            cubePosXSpan.textContent = cube.mat.e03.toFixed(2);
    
                            let cubePosYSpan = <HTMLSpanElement>document.querySelector("#cube" + cube.name + "_position_y");
                            cubePosYSpan.textContent = cube.mat.e13.toFixed(2);
    
                            let cubePosZSpan = <HTMLSpanElement>document.querySelector("#cube" + cube.name + "_position_z");
                            cubePosZSpan.textContent = cube.mat.e23.toFixed(2);
                        }
    
                        let num_quads = vertices.length / (4 * 9);
    
                        let genvidWebGL = this.genvidWebGL;
                        let cmd: RenderCommand = this.gfx_cmd_cubes;
                        cmd.vtx = genvidWebGL.createBuffer(new Float32Array(vertices));
                        cmd.idx = genvidWebGL.createIndexBufferForQuads(num_quads);
                    }
    
                    let mat = gameData.MatProjView;
                    this.gfx_prog_data_viewproj = [mat.e00, mat.e10, mat.e20, mat.e30, mat.e01, mat.e11, mat.e21, mat.e31, mat.e02, mat.e12, mat.e22, mat.e32, mat.e03, mat.e13, mat.e23, mat.e33];
    
                    this.gfx_draw3D();
                }
    
  9. その後、動画が他の操作を実行できる状態になっているかどうかを確認します。

  10. ビデオの準備ができている場合、Genvid オーバーレイ値を更新します。

  11. プロンプトのオーバーレイ (ユーザーのサウンド変更を表示するために使用) も更新します。

  12. 最後に、ビデオがフルスクリーンで表示されているかどうかを確認し、ステータスを更新します。

                if (this.videoReady) {
                    // Update the Genvid information overlay
                    let w = 18; // Width of the content of every line (without label).
                    let localTime: Date = new Date();
                    this.timeLocalDiv.textContent = `Local: ${this.msToDuration(Math.round(localTime.getTime()))}`;
    
                    let videoTimeRawMS = 0;
                    let videoPlayer = this.client.videoPlayer;
                    if (videoPlayer) {
                        videoTimeRawMS = videoPlayer.getCurrentTime() * 1000;
                    }
                    this.timeVideoRawDiv.textContent = `Raw Video: ${this.preN(this.msToDuration(Math.round(videoTimeRawMS)), w)}`;
    
                    this.timeVideoDiv.textContent = `Est. Video: ${this.preN(this.msToDuration(Math.round(this.client.videoTimeMS)), w)}`;
    
                    this.timeComposeLastDiv.textContent = `Last Compose: ${this.preN(this.msToDuration(Math.round(this.client.lastComposeTimeMS)), w)}`;
    
                    this.timeComposeDiv.textContent = `Est. Compose: ${this.preN(this.msToDuration(Math.round(this.client.composeTimeMS)), w)}`;
    
                    this.timeStreamDiv.textContent = `Stream: ${this.preN(this.msToDuration(Math.round(this.client.streamTimeMS)), w)}`;
    
                    this.latencyDiv.textContent = `Latency: ${this.preN(this.client.streamLatencyMS.toFixed(0), w - 3)} ms`;
    
                    this.delayOffsetDiv.textContent = `DelayOffset: ${this.preN(this.client.delayOffset.toFixed(0), w - 3)} ms`;
    
                    // Update the visibility on the overlay when using key press
                    if (this.promptOverlay.style.visibility === "visible" && this.timeVisiblePrompt < this.timeVisibleMax) {
                        this.timeVisiblePrompt++;
                        if (this.volumeChange === 2) {
                            this.volumeChange = 0;
                            this.promptOverlay.textContent = "Volume: " + this.client.videoPlayer.getVolume().toString() + " %";
                        } else if (this.volumeChange === 1) {
                            this.volumeChange = 0;
                            this.promptOverlay.textContent = "Volume: " + this.client.videoPlayer.getVolume().toString() + " %";
                        }
                    } else if (this.promptOverlay.style.visibility === "visible" && this.timeVisiblePrompt >= this.timeVisibleMax) {
                        this.promptOverlay.style.visibility = "hidden";
                    }
                }
                let isFullScreen = this.checkFullScreen();
                if (isFullScreen !== this.isFullScreen) {
                    this.isFullScreen = isFullScreen;
                }
    

initPlayerTable

initPlayerTable(cubeData: IGameDataCube[])

initPlayerTable は、各オブジェクトのストリームウィンドウの下にテーブルを作成します。

  1. 受信したゲームデータに基づいて、各オブジェクトの情報について繰り返すループ処理から始めます。

  2. 後で HTML タグに追加する文字列に、テーブルの HTML コードを追加します。

  3. 次に、テーブル、応援、リセットのクリック機能のイベントリスナーを追加します。

  4. 最後に、使用可能な色のループ処理を行い、クリックイベントリスナーを追加します。

            // Method used to display the appropriate number of players with their proper buttons
            initPlayerTable(cubeData: IGameDataCube[]) {
                for (let cube of cubeData) {
                    let cubeAdd = `<div class='col-md-6 col-lg-4 nopadding'>
                                            <div class='cube clickable cube` + cube.name + `'>
                                                <div>
                                                    <span class='cube_name clickable'>` + cube.name + `</span>
                                                    <button class='cheer' id='` + cube.name + `_cheerbutton'><i class='icon_like' aria-hidden='true'></i></button>
                                                    <span class='cheer_value cube` + cube.name + `_cheer'></span>
                                                </div>
                                                <div>
                                                    <span class='label clickable cube` + cube.name + `_reset'>Reset</span>
                                                    <span id='cube` + cube.name + `_position_x' class='cube_position'></span>
                                                    <span id='cube` + cube.name + `_position_y' class='cube_position'></span>
                                                    <span id='cube` + cube.name + `_position_z' class='cube_position'></span>
                                                </div>
                                                <table class='cube_color text-center'>
                                                    <tr>
                                                        <td class='clickable color` + cube.name + `_green'>Green</td>
                                                        <td class='clickable color` + cube.name + `_white'>White</td>
                                                        <td class='clickable color` + cube.name + `_yellow'>Yellow</td>
                                                    </tr>
                                                    <tr>
                                                        <td class='clickable color` + cube.name + `_darkblue'>Dark blue</td>
                                                        <td class='clickable color` + cube.name + `_grey'>Grey</td>
                                                        <td class='clickable color` + cube.name + `_lightblue''>Light Blue</td>
                                                    </tr>
                                                    <tr>
                                                        <td class='clickable color` + cube.name + `_orange'>Orange</td>
                                                        <td class='clickable color` + cube.name + `_blue'>Blue</td>
                                                        <td class='clickable color` + cube.name + `_purple'>Purple</td>
                                                    </tr>
                                                </table>
                                            </div>
                                        </div>`;
    
                    $(".gameControlsDiv").append(cubeAdd);
    
                    let cheerButton = <HTMLButtonElement>document.querySelector("#" + cube.name + "_cheerbutton");
                    cheerButton.addEventListener("click", (_event) => { this.onCheer(cube.name); }, false);
    
                    let cubeDiv = <HTMLDivElement>document.querySelector(".cube" + cube.name);
                    cubeDiv.addEventListener("click", (_event) => { this.onSelect(cube.name, true); }, false);
                    this.cubeDiv.push(cubeDiv);
    
                    let resetButton = <HTMLSpanElement>document.querySelector(".cube" + cube.name + "_reset");
                    resetButton.addEventListener("click", (_event) => { this.onReset(cube.name); }, false);
    
                    for (let colorSelect of this.tableColor) {
                        let colorButton = <HTMLSpanElement>document.querySelector(".color" + cube.name + "_" + colorSelect[0]);
                        colorButton.addEventListener("click", (_event) => { this.onColorChange(cube.name, colorSelect[1]); }, false);
                    }
                }
            }
    

on_streams_received

on_streams_received(dataStreams: genvid.IDataStreams)

on_streams_received メソッドがロープ処理を使用して、ゲームデータに関連付けられた最新のタイムコードを取得します。また、受信した JSON データを IGameData に変換します。これは、Web サイトがストリームを受信したときに呼び出します。

        private streamIdToFormat = {
            "GameData": "JSON",
            "Popularity": "JSON",
            "ColorChanged": "JSON",
            "GameCopyright": "UTF8"
        };
        // Upon receving the stream, get the timecode and the data
        private on_streams_received(dataStreams: genvid.IDataStreams) {
            for (let stream of [...dataStreams.streams, ...dataStreams.annotations]) {
                // Using switch...case because different operations can be made depending on the stream ID.
                switch (this.streamIdToFormat[stream.id]) {
                    case "JSON": {
                        // Parse the JSON from each elements
                        for (let frame of stream.frames) {
                            if (this.last_game_time_received < frame.timeCode) {
                                this.last_game_time_received = frame.timeCode;
                            }
                            let jsonOutput = this.tryParseJSON(frame.data);
                            if (jsonOutput) {
                                frame.user = jsonOutput;
                            }
                        }
                        break;
                    }
                    default:
                        break;
                }
            }
        }
        // Returns JSON parsed string or null if it couldn't be parsed.
        private tryParseJSON(str, streamId = null, streamSessionId = null): any {
            try {
                let retVal = JSON.parse(str);
                if (retVal && typeof retVal === "object") {
                    return retVal;
                }
            }
            catch (e) {
                if (streamId || streamSessionId) {
                    console.info(`invalid Json format on '${streamId}:${streamSessionId}' for '${str}' with error '${e}'`);
                } else {
                    console.info(`invalid Json format for '${str}' with error '${e}'`);
                }
            }
            return null;
        }

on_notifications_received

on_notifications_received(message: genvid.IDataNotifications)

on_notifications_received メソッドは、通知の ID が Popularity であるかどうかを確認します。Yes の場合、データを文字列に変換し、JSON データを ICubePopularity に変換します。Web サイトが通知を受信すると、このメソッドを呼び出します。

        // Upon receiving a notification, get the notification content
        private on_notifications_received(message: genvid.IDataNotifications) {
            for (let notification of message.notifications) {
                if (notification.id === "Popularity") {
                    let datastr = genvid.UTF8ToString(notification.rawdata);
                    try {
                        // Get the latest popularity
                        let userData = <ICubePopularities>JSON.parse(datastr);
                        this.latestPopularity = userData;
                    }
                    catch (err) {
                        console.info("invalid Json format for:" + datastr + " with error :" + err);
                    }

                }
            }
        }