Vovó ヘルプ

アクションの動き方

シーンがアクションを表示した瞬間から、Vovó がそれをどう扱うか。Vovó が保証することと、アクションが当てにしてはいけないことです。人に渡したり販売したりするアクションを書く人のための説明です。書き方は「アクション」にあります。

専用のページ

アクションは 1 つずつ独立したドキュメントで、シーンのページが用意するフレームに入ります。中身はアクションの HTML で、style.css と script.js が書き込まれ、スクリプトは最後に置かれるので、HTML がそろってから実行されます。1 つのアクションが壊れても、ほかは巻き込まれません。シーンはアクションをシーンの順番どおりに重ね、最初のものがいちばん下になります。

フレームはクリックもキーも受け取りません。オーバーレイは見るもので、押すものではありません。配信を操作する人がすることは、パッド、アクションのパネル、カスタマイズで行い、イベントとしてアクションに届きます。

アクションがイベントを聞くのは、開いているページがそのアクションを表示している間だけで、アクティブなプロファイルに限られます。ページが 1 つも開いていなければ、フォローを数えるものはありません。

ネットワークなし、ブラウザーには何も残さない

アクションが届くのは、自分のファイルと Vovó が渡すものだけです。画像、音、動画、フォントは、アクションのファイルから名前で読み込むか、イベントが伝えたアドレス(エモート、カバー画像、カスタマイズで選んだファイル)から読み込み、インターネットからは決して読み込みません。コードとスタイルは script.js と style.css だけです。ファイルや外部からのスクリプトやスタイルシートは使えず、eval もワーカーも使えません。fetch、XMLHttpRequest、WebSocket は、アクション自身のファイルに対しても拒否されるので、必要なデータはスクリプトに書きます。ブラウザーのストレージ(localStorage、Cookie、IndexedDB)はありません。アクションが残すものは vovo.state に残します。

外から必要なものは、宣言した権限の範囲で Vovó に頼みます。チャットの 1 行、名前で指定する Webhook、パッドの音、公式プレーヤーでのクリップなどです。

開いているすべてのページが実行する

同じアクションは、そのシーンが開いているすべての場所で動きます。OBS、シーンのプレビューウィンドウ、ブラウザーのタブ、同時に 2 つのソースなどです。どのコピーも同じイベントを受け取り、同じものを描きます。だからページは描画と再生だけを行い、1 回だけ起きるべきことは Vovó に頼みます。Vovó は、同じイベントの処理中に頼んできたページに対して 1 回だけ実行します。チャットの 1 行、抽選、音声、Webhook、パッドの音、OBS のソース、アクション独自のイベントがそうです。

Vovó は、処理中のイベントによって、あるページの呼び出しと別のページの呼び出しを見分けます。リスナーが戻った後の呼び出し(await の後、アニメーションが終わったとき、タイマーの中)は、{ event: e } で応答先のイベントを示します。e はリスナーが受け取ったオブジェクトです。これがないと、Vovó は 2 つ目のページなのか 2 回目なのかを区別できません。

vovo.on("raid", async (e) =>
{
    const gift = await vovo.draw(["a hug", "a song", "a dance"]);
    vovo.show("raid", { user: e.user, gift: gift.value });
    vovo.chat.send(e.user + " gets " + gift.value + "!", { event: e });
});

ページが自分で始めることには、背後にイベントがありません。アクションの setInterval はすべてのページで動き、読み込み時に行うことも同じです。その場合 Vovó は頼まれた内容でしか判断できません(5 秒以内の同じチャットの行、同じ音声、同じ Webhook、同じ独自イベントは 1 つとして扱います)。数えるものや 1 回だけ起きるべきことは timer トリガーに置きます。これはアプリのもので、すべてのページで 1 つです。

状態であって、再生し直しではない

vovo.state を保持するのはページではなく Vovó です。ページが読み込まれると(OBS の起動、ソースの更新、パニックの終了、Vovó の再起動)、スクリプトが動く前に状態がすべてそろっています。あるページで設定したキーは、ほかのページにもすぐに、何も知らせずに届きます。ほかのページも同じイベントを自分で処理しているからです。イベントが再生し直されることはありません。レイドが来たときにいなかったページは、後からそれを知ることはありません。だからアクションは、もう一度表示する必要があるもの(目標のカウント、抽選の名前、カウントダウンの終了時刻)をすべて状態に入れ、読み込み時にそこから描きます。

function draw()
{
    document.getElementById("count").textContent = vovo.state.count || 0;
}

vovo.on("follow", () =>
{
    vovo.state.count = (vovo.state.count || 0) + 1;
    draw();
});

draw();

現在の値と、流れ続ける読み取り値

4 つのイベントは、ある瞬間であると同時に値でもあります:obs.scene、media.track、twitch.viewers、screen.shared。読み込まれたアクションは、聞いているものそれぞれの最新の値を、たった今起きたかのようにすぐ受け取ります。だから次の曲を待たずに再生中の曲を表示できます。これは読み込みのたびに、すべてのページで、背後にイベントがない状態で届きます。そこから描画し、1 回だけ起きるべきことで応答しないでください。

audio.level、screen.color、input.move、speech.text は読み取り値で、最大で毎秒 60 回届きます。届けられて忘れられ、開いているページがそれを聞くアクションを表示している間だけ読み取られます。リスナーは軽くし、動きは CSS に任せましょう。

読み込み、再読み込み、パニック

ページはいつでも、何度でも読み込まれます。OBS はシーンが表示されるとソースを開始し、隠れている間は停止することがあり、再び表示されると更新することがあります。Vovó も、アクションのコードやファイルが変わると読み込み直します。パニックはすべてのシーンを一度に消してアクションを読み込み直し、アクションがしていたことはそこで止まります。パニックが終わると、もう一度読み込まれて状態から始まります。パニック中はアクションに何も届かず、その間に来たものが後から再生されることもありません。コントロールの値は状態なので保持されます。

つまりアクションは、前のページがやりかけで残したものを当てにせず、いつでも状態だけから正しく立ち上がる必要があります。

ステージ

アクションは、短辺が 1080 の CSS ピクセルのステージに描きます。16:9 の配信なら解像度にかかわらず 1920 × 1080(4K のソースは 2 倍の大きさでくっきり描きます)、縦型の配信なら 1080 × 1920、ほかの形の画面ならその形になります。アクションのドキュメントはステージと同じ大きさで透明なので、100vw × 100vh が画面全体になり、描かなかった部分にはゲームが見えます。ほかの人の配信向けに作るアクションは、形を決めつけません。端からの位置やパーセントで配置し、縦型の配信には @media (orientation: portrait) を使います。

OBS のブラウザー

OBS は独自のブラウザーでシーンを表示します。Vovó のエディターやプレビューウィンドウのものより古く(OBS 32 では Chromium 127)、グラフィックカードの支援なしで動くこともあります。新しい CSS や JavaScript の機能は、頼る前にこのバージョンで使えるか確認し、渡す前に OBS そのものでアクションを見てください。OBS では音がクリックなしですぐに鳴り、配信に流れます。配信者だけに向けたものはアクションに入れません。

OBS のページにはコンソールがありません。アクションが捕まえなかったエラーと、呼び出しについて Vovó が伝えることは、アクションの名前と行番号付きで Vovó のコンソールに届きます。console.log は届きません。それを見るには、プレビューのアドレス(vovo dev が表示します)をブラウザーで開き、開発者ツールを使います。

ペースと上限

ペースを超えた呼び出しは捨てられ、順番待ちにはなりません。その Promise は理由とともに拒否されます。

渡す前に