WebController - Enter フレーム

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

onNewFrame

onNewFrame(frameSource)

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

  1. まず、 updateOverlays(compositionData) を用いてオーバーレイを更新し、ビデオストリームの構成に合わせてオーバーレイを更新します。ピクチャモードでピクチャ内のオーバーレイをクロッピングすることができます。
      updateOverlays(compositionData) {
        let multipleSourceVideoComposition = false;
        let hideOverlay = false;
        // Do we have multiple sources?
        if (compositionData && compositionData.length > 1) {
          // Assuming the second element to be the foreground frame.
          if (compositionData[1].type == "PipVideoLayout") {
            // Prevent the 3d overlay to overlay the secondary screen
            this.pipFrameDiv.style.display = "block";
            hideOverlay = true;
            multipleSourceVideoComposition = true;
            // set up the main canvas to pip affine transform matrix
            const pipMat = genvidMath.mat3FromArray(
              compositionData[1].affineMatrix
            );
            this.updateDomRect(this.pipFrameDiv, this.videoOverlay, pipMat);
            this.updateDomClipping(this.canvas3d, pipMat);
          } else if (compositionData[1].type == "ChromaKeyVideoLayout") {
            multipleSourceVideoComposition = true;
          }
        }
        if (!hideOverlay) {
          this.pipFrameDiv.style.display = "none";
          if (this.canvas3d) this.canvas3d.style.removeProperty("clip-path");
        }
        return multipleSourceVideoComposition;
      }
    
  2. アノテーションデータを取得します。有効かどうかを確認し、その後コンソールログを表示します。

  3. 動画が操作を実行できる状態になっているかどうかを確認します。

  4. ビデオの準備ができたら、さまざまな処理を行う前に、ゲームデータを取得してから、そのデータが有効であることを確認します。

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

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

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

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

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

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

        if (this.videoReady) {
          if (gameDataFrame && gameDataFrame.user) {
            const gameData = gameDataFrame.user;
            this.lastGameData = gameData;
            const cubes = gameData.cubes;
    
            // Lets update the time of camera and scene. Only for the Unity sample.
            if (gameData.camera && gameData.scene) {
              this.timeCamSceneDiv.textContent = ` Time until next camera: ${gameData.camera.toFixed(
                2
              )} seconds
                    Time until next scene: ${gameData.scene.toFixed(2)} seconds`;
            }
    
            // Performs the webgl update process -- Update 3d
            if (cubes) {
              // Sets up the player table once the game data is ready
              if (this.cubePanelDiv.length === 0) {
                this.initPlayerTable(cubes);
              }
    
              const vertices = [];
              const projViewMatrix = this.convertMatrix(gameData.MatProjView);
              for (const cube of cubes) {
                const m = cube.mat;
                const p = genvidMath.vec3(m[12], m[13], m[14]);
                const r = this.circleRadius;
                let c = genvidMath.vec4(1, 0, 0, 1);
                if (cube.color) {
                  c = genvidMath.vec4(
                    cube.color[0],
                    cube.color[1],
                    cube.color[2],
                    cube.color[3]
                  );
                }
                if (this.isSelected(cube.name)) {
                  c = genvidMath.muls4D(c, 2.0); // brighten selected color intensity
                } else {
                  c = genvidMath.muls4D(c, 0.8); // dim unselected color intensity
                }
                this.makeCircleZ(vertices, p.x, p.y, p.z, r, c);
    
                // Makes the name tag of the cube follow the cube
                const tag = this.findOrCreateTagDiv(cube);
                const pos2d = genvidMath.projectPosition(projViewMatrix, p);
                this.centerAt(tag, pos2d, genvidMath.vec2(0, -75));
    
                // Modifies the popularity with the latest value
                if (this.latestPopularity) {
                  const pCube = this.latestPopularity.find((c) => {
                    return c.name === cube.name;
                  });
                  if (pCube) {
                    cube.popularity = pCube.popularity;
                  }
                }
    
                let cubePopSpan = document.querySelector(
                  `#${cube.name} .cheer_value`
                );
                cubePopSpan.textContent = this.popularityToText(cube.popularity);
    
                let cubePosXSpan = document.querySelector(
                  `#${cube.name} .position_x`
                );
                cubePosXSpan.textContent = cube.mat[12].toFixed(2);
    
                let cubePosYSpan = document.querySelector(
                  `#${cube.name} .position_y`
                );
                cubePosYSpan.textContent = cube.mat[13].toFixed(2);
    
                let cubePosZSpan = document.querySelector(
                  `#${cube.name} .position_z`
                );
                cubePosZSpan.textContent = cube.mat[14].toFixed(2);
              }
    
              let numQuads = vertices.length / (4 * 9);
    
              this.gfxCmdCubes.vtx = this.genvidWebGL.createBuffer(
                new Float32Array(vertices)
              );
              this.gfxCmdCubes.idx = this.genvidWebGL.createIndexBufferForQuads(
                numQuads
              );
            }
    
            this.gfxProgDataViewproj = gameData.MatProjView;
    
            this.gfxDraw3D();
          }
    
  11. その後、Genvid オーバーレイの値とプロンプトオーバーレイの可視性 (ユーザーの変更をサウンドに表示するために使用) を更新します。

          // Updates the Genvid information overlay
          let width = 18; // Width of the content of every line (without label).
          this.timeLocalDiv.textContent = `Local: ${this.preN(
            this.msToDuration(Date.now()),
            width,
            " "
          )}`;
    
          this.timeVideoDiv.textContent = `Est. Video: ${this.preN(
            this.msToDuration(Math.round(this.genvidClient.videoTimeMS)),
            width,
            " "
          )}`;
    
          this.timeComposeLastDiv.textContent = `Stream received: ${this.preN(
            this.msToDuration(Math.round(this.genvidClient.lastComposeTimeMS)),
            width,
            " "
          )}`;
    
          this.timeStreamDiv.textContent = `Stream played: ${this.preN(
            this.msToDuration(Math.round(this.genvidClient.streamTimeMS)),
            width,
            " "
          )}`;
    
          this.latencyDiv.textContent = `Latency: ${this.preN(
            this.genvidClient.streamLatencyMS.toFixed(0),
            width - 3,
            " "
          )} ms`;
    
          this.delayOffsetDiv.textContent = `DelayOffset: ${this.preN(
            this.genvidClient.delayOffset.toFixed(0),
            width - 3,
            " "
          )} ms`;
    
          // Updates the visibility on the overlay when using key press
          if (
            this.volumeDisplay.style.visibility === "visible" &&
            this.volumeInfoDisplayCount < this.volumeInfoDisplayUntil
          ) {
            this.volumeInfoDisplayCount++;
          } else if (
            this.volumeDisplay.style.visibility === "visible" &&
            this.volumeInfoDisplayCount >= this.volumeInfoDisplayUntil
          ) {
            this.volumeDisplay.style.visibility = "hidden";
          }
        }
    

initPlayerTable

initPlayerTable(cubeData)

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

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

  2. あとで HTML タグに追加するノードを複製します。

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

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

      // Method used to display the appropriate number of players with their proper buttons
      initPlayerTable(cubeData) {
        let cubePanel = document.getElementById("cube_panel_prototype");
    
        for (let cube of cubeData) {
          let cubePanelClone = cubePanel.cloneNode(true);
          cubePanelClone.id = cube.name;
          cubePanelClone.getElementsByClassName("cube_name")[0].innerText =
            cube.name;
    
          let cheerButton = cubePanelClone.querySelector(".cheer");
          cheerButton.addEventListener(
            "click",
            () => this.onCheer(cube.name),
            false
          );
    
          let cubeDiv = cubePanelClone.querySelector(".cube");
          cubeDiv.addEventListener("click", () => this.onSelect(cube.name), false);
          this.cubePanelDiv.push(cubeDiv); // will stop triggering initPlayerTable from onNewFrame() indefinitely
    
          let resetButton = cubePanelClone.querySelector(".reset");
          resetButton.addEventListener(
            "click",
            () => this.onReset(cube.name),
            false
          );
    
          for (let colorSelect of this.tableColor) {
            let colorButton = cubePanelClone.querySelector("." + colorSelect[0]);
            colorButton.addEventListener(
              "click",
              () => this.onColorChange(cube.name, colorSelect[1]),
              false
            );
          }
          document.querySelector(".gameControlsDiv").append(cubePanelClone);
        }
        cubePanel.remove();
      }
    

onStreamsReceived

onStreamsReceived(dataStreams)

onStreamsReceived メソッドはループを使ってゲームデータを取得します。最初のループで受け取ったフォーマットが JSON の場合、データを JavaScript に変換します。フォーマットが UTF8 で ID が GameCopyright の場合は、 console.log で表示します。2 つ目のループでは、アノテーションの JSON データを JavaScript に変換します。Web サイトがストリームを受信したときに onStreamsReceived を呼び出します。

  // Upon receving the stream, gets the data
  onStreamsReceived(dataStreams) {
    for (let stream of dataStreams.streams) {
      // 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) {
            frame.user = JSON.parse(frame.data);
          }
          break;
        }
        case "UTF8": {
          // Code handling UTF8 data format.
          for (let frame of stream.frames) {
            if (stream.id == "GameCopyright") genvid.log(frame.data);
          }
          break;
        }
        default:
          break;
      }
    }
    for (let annotation of dataStreams.annotations) {
      // Using switch...case because different operations can be made depending on the stream ID.
      switch (this.annotationIdToFormat[annotation.id]) {
        case "JSON": {
          // Parse the JSON from each elements
          for (let frame of annotation.frames) {
            frame.user = JSON.parse(frame.data);
          }
          break;
        }
        default:
          break;
      }
    }
  }

onNotificationsReceived

onNotificationsReceived(message)

onNotificationsReceived` メソッドは通知の ID が Popularity であるかどうかを調べ、その際に JSON データを JavaScript に変換します。また、Web サイトが通知を受信したときにこのメソッドを呼び出します。

  // Upon receiving a notification, gets the notification content
  onNotificationsReceived(message) {
    for (let notification of message.notifications) {
      if (notification.id === "Popularity") {
        this.latestPopularity = JSON.parse(notification.data).cubes;
      }
    }
  }