WebController - Enter フレーム¶
Enter フレームセクションには、各フレームまたは同様の方法でトリガされるメソッドが含まれています。
In This Section
onNewFrame¶
onNewFrame(frameSource)
onNewFrame メソッドは、常に更新が必要な様々なタスクを実行します。作成した Genvid クライアント用に、フレームごとに呼び出します。
- まず、 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; }
アノテーションデータを取得します。有効かどうかを確認し、その後コンソールログを表示します。
動画が操作を実行できる状態になっているかどうかを確認します。
ビデオの準備ができたら、さまざまな処理を行う前に、ゲームデータを取得してから、そのデータが有効であることを確認します。
データの妥当性が確認できたら、データ (キューブ) からオブジェクトリストを取得します。
次のカメラとシーンまでの残り時間のテキストフィールドを更新します。
プレイヤーテーブルをセットアップしたかどうかを確認します。設定されていない場合、
initPlayerTable
を呼び出します。オブジェクト配列を使用して、WebGL で輪を作るループ処理を作成します。
同じループで、各オブジェクトの popularity 値と位置を更新します。
ループ処理の終了後、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(); }
その後、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 は、各オブジェクトのストリームウィンドウの下にテーブルを作成します。
受信したゲームデータに基づいて、各オブジェクトの情報について繰り返すループ処理から始めます。
あとで HTML タグに追加するノードを複製します。
次に、テーブル、応援、リセットのクリック機能のイベントリスナーを追加します。
最後に、使用可能な色のループ処理を行い、クリックイベントリスナーを追加します。
// 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;
}
}
}