mcpField
Y
youtube-playables-js-sdk

youtube-playables-js-sdk

github.com/hungify開発016 回インストール1 週間前に更新
リポジトリ

youtube-playables-js-sdk について

youtube-playables-js-sdkは、YouTube Playables JavaScript SDKをゲームに組み込むためのスキルです。ゲーム開発者向けに、初期化、セーブデータ、広告、スコア送信の手順を説明します。

主な機能

  1. 01ytgameグローバルの待機
  2. 02ゲーム準備完了APIの順次呼び出し
  3. 03セーブデータの読み書き
  4. 04インタースティシャル広告とリワード広告の要求
  5. 05整数のランキングスコア送信

活用シーン

  1. 01ゲームへのYouTube Playables統合
  2. 02プレイヤーデータの保存と更新
  3. 03広告フローとスコア送信の追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/hungify/playables-skills --skill youtube-playables-js-sdk

インストール後、Claude Code で/youtube-playables-js-sdkを入力して読み込まれたか確認します。

詳細

YouTube Playables JavaScript SDK

インストール

ytgame グローバルは YouTube ホストによって注入されるため、インストールは不要です。表示されるまでポーリングしてから、解決します。

function waitForYtgame(timeoutMs = 10000) {
    return new Promise((resolve, reject) => {
        if (window.ytgame) {
            resolve(window.ytgame);
            return;
        }

        const deadline = Date.now() + timeoutMs;
        const intervalId = setInterval(() => {
            if (window.ytgame) {
                clearInterval(intervalId);
                resolve(window.ytgame);
            } else if (Date.now() >= deadline) {
                clearInterval(intervalId);
                reject(new Error('ytgame SDK not available — running outside YouTube Playables?'));
            }
        }, 100);
    });
}

初期化

手順:ytgame を待つ → 言語の取得と保存データの読み込みを並行して行う → firstFrameReady() を呼び出す → 完全に操作可能になったら gameReady() を呼び出す。

let ytgame = null;
let platformLanguage = 'en';
let savedData = {};

waitForYtgame()
    .then((sdk) => {
        ytgame = sdk;

        const getLanguagePromise = ytgame.system.getLanguage().then((language) => {
            platformLanguage = language.length > 2 ? language.slice(0, 2) : language;
        });

        const getDataPromise = ytgame.game.loadData().then((data) => {
            if (typeof data === 'string' && data !== '') {
                try {
                    savedData = JSON.parse(data);
                } catch (e) {
                    console.error('Failed to parse saved data, starting fresh', e);
                    savedData = {};
                }
            }
        });

        ytgame.system.onAudioEnabledChange((isEnabled) => {
            // Mute or unmute the game audio based on isEnabled
        });

        ytgame.system.onPause(() => {
            // Pause the game loop and audio
        });

        ytgame.system.onResume(() => {
            // Resume the game loop and audio
        });

        return Promise.all([getLanguagePromise, getDataPromise]);
    })
    .then(() => {
        ytgame.game.firstFrameReady();
    })
    .catch((err) => {
        console.error('YouTube Playables SDK initialization failed', err);
        // Fall back to a non-Playables code path if needed
    });

ゲームが完全に操作可能になったら:

function notifyGameReady() {
    ytgame.game.gameReady();
}

ストレージ(保存データ)

保存データは、プレイヤーごとに 1 つの JSON シリアライズ可能なデータです。関係のないキーを上書きしないよう、必ず読み取り → マージ → 書き込みの順で処理してください。書き込み後、再度読み取って保存を確認します。

// Read one or many keys from saved data
function getDataFromStorage(key, tryParseJson) {
    return ytgame.game.loadData().then((data) => {
        if (typeof data === 'string' && data !== '') {
            try {
                savedData = JSON.parse(data);
            } catch (e) {
                console.error('loadData parse error', e);
            }
        }

        const readOne = (k) => {
            let value = typeof savedData[k] === 'undefined' ? null : savedData[k];
            if (typeof value === 'string' && tryParseJson) {
                try {
                    value = JSON.parse(value);
                } catch (e) {
                    /* keep as-is */
                }
            }
            return value;
        };

        return Array.isArray(key) ? key.map(readOne) : readOne(key);
    });
}

// Write one or many keys, then verify by reloading
function setDataToStorage(key, value) {
    const data = savedData !== null ? { ...savedData } : {};

    if (Array.isArray(key)) {
        for (let i = 0; i < key.length; i++) data[key[i]] = value[i];
    } else {
        data[key] = value;
    }

    return ytgame.game
        .saveData(JSON.stringify(data))
        .then(() => {
            savedData = data;
            return ytgame.game.loadData();
        })
        .then((persisted) => {
            if (persisted !== JSON.stringify(data)) {
                console.warn(
                    'saveData verification mismatch — persisted value differs from written value'
                );
            }
        })
        .catch((err) => {
            console.error('saveData failed', err);
        });
}

// Delete one or many keys
function deleteDataFromStorage(key) {
    const data = savedData !== null ? { ...savedData } : {};

    if (Array.isArray(key)) {
        for (const k of key) delete data[k];
    } else {
        delete data[key];
    }

    return ytgame.game
        .saveData(JSON.stringify(data))
        .then(() => {
            savedData = data;
        })
        .catch((err) => {
            console.error('deleteData (saveData) failed', err);
        });
}

広告

インタースティシャル広告

ytgame.ads.requestInterstitialAd() は、広告フローが終了すると解決します。リクエストの前に一時停止し、解決または拒否された後に再開してください。

function showInterstitial() {
    // Pause game and mute audio before requesting
    ytgame.ads
        .requestInterstitialAd()
        .then(() => {
            // Resume game and unmute audio
        })
        .catch(() => {
            // Ad failed to deliver — resume game and continue
        });
}
リワード広告

ytgame.ads.requestRewardedAd(placement) はブール値で解決します。解決された値が真の場合にのみ、報酬を付与してください。

function showRewarded(placement) {
    // Pause game and mute audio before requesting
    ytgame.ads
        .requestRewardedAd(placement)
        .then((isRewardEarned) => {
            if (isRewardEarned) {
                // Grant reward to the player
            }
            // Resume game and unmute audio
        })
        .catch(() => {
            // Ad failed to deliver — resume game and continue
        });
}

リーダーボード

ytgame.engagement.sendScore({ value }) を使って整数のスコアを送信します。送信前に文字列を整数に変換してください。

function submitScore(score) {
    const value = typeof score === 'string' ? parseInt(score, 10) : score;
    return ytgame.engagement.sendScore({ value });
}

コメント