y8-js-sdk について
y8-js-sdk は、JavaScript ゲームに Y8 のプレイヤー、クラウドストレージ、広告、実績、ランキング API を組み込むためのガイドです。Y8 ゲーム開発者向けに、確認手順を伴う導入フローを示します。
主な機能
- 01Y8 SDK の読み込みと初期化
- 02ログイン状態と認証の確認
- 03プレイヤーデータのクラウド保存
- 04インタースティシャル広告とリワード広告
- 05実績の解除と検索
- 06スコア送信とランキング取得
活用シーン
- 01ゲーム内でプレイヤーのログイン情報を取得
- 02認証済みプレイヤーのデータをクラウド保存
- 03インタースティシャル広告やリワード広告を表示
- 04スコアを送信して実績を表示
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hungify/ playables-skills --skill y8-js-sdkインストール後、Claude Code で/y8-js-sdkを入力して読み込まれたか確認します。
詳細
Y8 JavaScript SDK
統合ワークフロー
以下の手順を順番に実行してください。各手順には、次に進む前の検証チェックポイントが含まれています。
- SDK を読み込む — スクリプトを挿入し、
window.IDをポーリングします。- ✓ 確認:
typeof window.ID !== 'undefined'
- ✓ 確認:
ID.init({ appId })を呼び出す前に、id.initをサブスクライブします。- ✓ 確認:
id.initのコールバックが実行されること。実行されない場合は、appIdを確認してください。
- ✓ 確認:
- プレイヤーのプロフィールを設定するため、
id.init内でログイン状態を取得します。- ✓ 確認:
data.status === 'ok'は、プレイヤーがサインイン済みであることを意味します。
- ✓ 確認:
id.init内で、ログイン状態の取得と並行して AdSense を読み込みます。- ✓ 確認:
adsbygoogleスクリプトが読み込まれ、onReadyが実行されること。
- ✓ 確認:
profile.isAuthorized === trueの場合に限り、ストレージ、実績、リーダーボードを利用します。- ✓ 各 API 呼び出しの前に確認:プレイヤーが認証済みであること。未認証の場合は、先にログインを促してください。
ID.loginの呼び出しが成功した後、cachedUserDataを無効化(nullに設定)し、新しいセッションで自身のクラウド状態を読み込むようにします。
インストール
const SDK_URL = 'https://cdn.y8.com/api/sdk.js';
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load ' + src));
document.head.appendChild(script);
});
}
function waitFor(globalName, timeoutMs = 10000) {
return new Promise((resolve, reject) => {
const started = Date.now();
const tick = () => {
if (typeof window[globalName] !== 'undefined') {
resolve(window[globalName]);
return;
}
if (Date.now() - started > timeoutMs) {
reject(new Error(globalName + ' is not available'));
return;
}
setTimeout(tick, 50);
};
tick();
});
}
初期化
まず id.init をサブスクライブし、その後 ID.init({ appId }) を呼び出します。appId(別名 Game API Key)は、ゲームの登録時に Y8 から発行されます。id.init が実行されたら、ログイン状態の取得と AdSense の読み込みを並行して行います。
async function initY8({ gameId, adSenseId, channelId }) {
if (!gameId) {
throw new Error('gameId (Y8 appId) is required');
}
await loadScript(SDK_URL);
const sdk = await waitFor('ID'); // ✓ checkpoint: window.ID exists
return new Promise((resolve, reject) => {
sdk.Event.subscribe('id.init', () => {
// ✓ checkpoint: id.init fired — SDK is ready
loadAdsByGoogle({ adSenseId, channelId })
.then((showAd) => {
sdk.getLoginStatus((data) => {
const profile = readPlayerInfo(data);
resolve({ sdk, showAd, profile });
});
})
.catch(reject);
});
sdk.init({
appId: gameId
});
});
}
プレイヤー/認証
ID.getLoginStatus(callback) は、プロンプトを表示せずに現在のセッションを返します。ID.login(callback) は、Y8 のログインダイアログを開きます。どちらも同じ形式のペイロードを返します。
function readPlayerInfo(data) {
const profile = {
isAuthorized: false,
id: null,
name: null,
locale: null,
photos: []
};
if (!data || data.status !== 'ok') {
return profile;
}
const details = data.authResponse.details;
const { pid, locale, nickname, first_name: firstName, last_name: lastName, avatars } = details;
profile.isAuthorized = true;
profile.id = pid || null;
profile.locale = locale || null;
profile.name = [firstName, lastName].filter((x) => !!x).join(' ') || nickname || null;
const { thumb_url: photoSmall, medium_url: photoMedium, large_url: photoLarge } = avatars || {};
if (photoSmall) profile.photos.push(photoSmall);
if (photoMedium) profile.photos.push(photoMedium);
if (photoLarge) profile.photos.push(photoLarge);
return profile;
}
function authorizePlayer(sdk) {
return new Promise((resolve, reject) => {
sdk.login((response) => {
const profile = readPlayerInfo(response);
if (response && response.status === 'ok') {
resolve(profile);
} else {
reject(new Error('Y8 login was cancelled or failed'));
}
});
});
}
ストレージ
クラウド上のユーザーデータには ID.api() を通じてアクセスします。すべてのゲームデータを、1 つのキーの下に単一の JSON オブジェクトとして保存します。一度読み込み、ローカルで変更してから書き戻してください。
ログイン後:
cachedUserData = nullを設定し、新たに認証されたプレイヤーが自身のクラウド状態を読み込むようにします。
const USERDATA_KEY = 'userData';
const NOT_FOUND_ERROR = 'Key not found';
let cachedUserData = null;
function loadUserData(sdk) {
return new Promise((resolve, reject) => {
if (cachedUserData) {
resolve(cachedUserData);
return;
}
sdk.api('user_data/retrieve', 'POST', { key: USERDATA_KEY }, (response) => {
if (response.error && response.error !== NOT_FOUND_ERROR) {
reject(response);
return;
}
let userData = {};
try {
if (response.jsondata) {
userData = JSON.parse(response.jsondata);
}
} catch (e) {
// Leave userData as an empty object on parse failure
}
cachedUserData = userData;
resolve(userData);
});
});
}
function saveUserData(sdk, userData) {
return new Promise((resolve, reject) => {
const payload = { key: USERDATA_KEY, value: JSON.stringify(userData) };
sdk.api('user_data/submit', 'POST', payload, (response) => {
if (response.status === 'ok') {
cachedUserData = userData;
resolve();
} else {
reject(response);
}
});
});
}
function getStorageValue(sdk, key) {
return loadUserData(sdk).then((userData) => userData[key] ?? null);
}
function setStorageValue(sdk, key, value) {
return loadUserData(sdk).then((userData) => {
const next = { ...userData, [key]: value };
return saveUserData(sdk, next);
});
}
function deleteStorageValue(sdk, key) {
return loadUserData(sdk).then((userData) => {
const next = { ...userData };
delete next[key];
return saveUserData(sdk, next);
});
}
広告
adsbygoogle.push は、インタースティシャル広告用の type: 'start' と、リワード広告用の type: 'reward' を備えた adBreak API を提供します。Y8 からチャネル ID が提供されている場合は data-ad-channel を使用し、提供されていない場合は AdSense のクライアント ID を使用します。
const ADS_ID = '6129580795478709';
function loadAdsByGoogle({ adSenseId, channelId }) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
script.setAttribute('crossorigin', 'anonymous');
script.setAttribute('data-ad-frequency-hint', '180s');
script.setAttribute('data-ad-client', channelId ? `ca-pub-${ADS_ID}` : adSenseId);
if (channelId) {
script.setAttribute('data-ad-channel', channelId);
} else {
script.setAttribute('data-ad-host', `ca-host-pub-${ADS_ID}`);
}
script.addEventListener('load', () => {
window.adsbygoogle = window.adsbygoogle || [];
window.adsbygoogle.push({
preloadAdBreaks: 'on',
sound: 'on',
onReady: () => {}
});
resolve((adOptions) => window.adsbygoogle.push(adOptions));
});
script.addEventListener('error', () => reject(new Error('adsbygoogle failed to load')));
document.head.appendChild(script);
});
}
インタースティシャル
function showInterstitial(showAd, { onOpened, onClosed, onFailed } = {}) {
if (!showAd) {
onFailed?.('ads_not_ready');
return;
}
showAd({
type: 'start',
name: 'start-game',
beforeAd: () => {
onOpened?.();
},
afterAd: () => {
onClosed?.();
},
adBreakDone: (placementInfo) => {
if (placementInfo.breakStatus !== 'viewed') {
onFailed?.(placementInfo.breakStatus);
}
}
});
}
リワード広告
beforeReward(showAdFn) で広告を制御します。広告を表示するには showAdFn(0) を呼び出してください。プレイヤーが広告を最後まで視聴すると adViewed が実行され、途中で終了すると adDismissed が実行されます。
function showRewarded(showAd, { onOpened, onRewarded, onClosed, onFailed } = {}) {
if (!showAd) {
onFailed?.('ads_not_ready');
return;
}
showAd({
type: 'reward',
name: 'rewarded Ad',
beforeAd: () => {
onOpened?.();
},
afterAd: () => {
onClosed?.();
},
beforeReward: (showAdFn) => {
showAdFn(0);
},
adDismissed: () => {
onFailed?.('dismissed');
},
adViewed: () => {
onRewarded?.();
},
adBreakDone: (placementInfo) => {
if (
placementInfo.breakStatus === 'frequencyCapped' ||
placementInfo.breakStatus === 'other'
) {
onFailed?.(placementInfo.breakStatus);
}
}
});
}
実績
Y8 のネイティブ実績は ID.GameAPI.Achievements の下にあります。achievement(表示名)と achievementkey(変更されない ID)の両方が必須です。
ロック解除
function unlockAchievement(sdk, options) {
if (!options || !options.achievement || !options.achievementkey) {
return Promise.reject(new Error('achievement and achievementkey are required'));
}
return new Promise((resolve) => {
sdk.GameAPI.Achievements.save(options, (data) => {
resolve(data);
});
});
}
実績の一覧取得(カスタムクエリ)
listCustom は、ネストされた player オブジェクトを含む実績を返します。扱いやすいよう、フラット化してください。
function getAchievementsList(sdk, options = {}) {
return new Promise((resolve, reject) => {
sdk.GameAPI.Achievements.listCustom(options, (data) => {
if (!data.success) {
reject(new Error(data.errorcode));
return;
}
const list = data.achievements.map(({ player, ...achievement }) => ({
...achievement,
playerid: player.playerid,
playername: player.playername,
lastupdated: player.lastupdated,
date: player.date,
rdate: player.rdate
}));
resolve(list);
});
});
}
ネイティブポップアップ
ID.GameAPI.Achievements.list(options) は、Y8 の組み込み実績 UI を開き、値を返しません。
function showAchievementsPopup(sdk, options = {}) {
sdk.GameAPI.Achievements.list(options);
}
リーダーボード
各ボードは、Y8 ダッシュボードで設定された文字列の table ID で識別されます。
スコアの送信
function submitScore(sdk, tableId, score) {
return new Promise((resolve, reject) => {
const options = {
table: tableId,
points: score
};
sdk.GameAPI.Leaderboards.save(options, ({ success, errormessage }) => {
if (success) {
resolve();
} else {
reject(new Error(errormessage || 'leaderboard_save_failed'));
}
});
});
}
エントリーの取得
有効な mode の値:'alltime'(デフォルト)、'daily'、'weekly'、'monthly'。
function getLeaderboardEntries(sdk, tableId) {
return new Promise((resolve, reject) => {
const options = {
table: tableId,
mode: 'alltime'
};
sdk.GameAPI.Leaderboards.listCustom(options, ({ scores, success, errormessage }) => {
if (!success) {
reject(new Error(errormessage || 'leaderboard_list_failed'));
return;
}
const entries = scores.map((entry) => ({
id: entry.playerid,
name: entry.playername,
score: entry.points,
rank: entry.rank,
photo: null
}));
resolve(entries);
});
});
}