リワード広告を導入する

このガイドでは、Michao!!のリワード広告を導入する方法についての詳細なドキュメントを提供します。

はじめに

このガイドでは、Michao!!のリワード広告を導入する方法について解説します。

前提条件

  • 管理画面にてサイトとプレースメントを事前に作成してください。

リワード広告の仕組み

リワード広告は、ユーザーが広告を視聴することで報酬を得られる広告形式です。導入には以下の流れが必要です。

実装手順

1. スクリプトの初期化

HTML の<head>タグ内に以下のコードを配置します。

<script src="https://cdn.adlookmanager.com/manager.js" async></script>

<script>
  window.alm = window.alm || { que: [] };
</script>

2. 広告枠の定義 (広告の読み込み)

広告を表示する要素とプレースメント ID を関連づけます。 この時点で広告が読み込まれます。

window.alm.que.push(function () {
  window.alm.defineAdSlot("YOUR_PLACEMENT_ID", "TARGET_ELEMENT_ID");
});

ヒント: プレースメント ID の取得は管理画面から簡単にできます。

  1. 管理画面で該当プレースメントを開く
  2. メニューから「タグを生成」を選択
  3. 生成されたコードをコピー

3. 広告の表示

ユーザーアクションなどのタイミングで広告を表示します。

window.alm.que.push(function () {
  window.alm.render("TARGET_ELEMENT_ID");
});

4. イベントの処理

広告に関連するイベントを処理し、ユーザーに適切な報酬を提供します。

window.alm.que.push(function () {
  window.alm.getAdTracker("TARGET_ELEMENT_ID").then(function (adTracker) {
    // リワード獲得時の処理
    adTracker.track(window.alm.Events.REWARDED, function () {
      console.log("リワード獲得!ユーザーに報酬を付与します");
      // ここにリワード付与のロジックを実装
    });

    // エラー発生時の処理
    adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
      console.log("広告の読み込みに失敗しました");
      // ここにエラー処理を実装
    });
  });
});

実装例

以下に簡単な実装例を紹介します。

<!DOCTYPE html>
<html>
  <head>
    <title>Michao!! リワード広告デモ</title>
    <script src="https://cdn.adlookmanager.com/manager.js" async></script>
    <script>
      window.alm = window.alm || { que: [] };
    </script>
  </head>
  <body>
    <h1>リワード広告デモ</h1>

    <div id="reward-ad-container"></div>
    <button id="show-ad-button">広告を見てポイントをゲット!</button>

    <script>
      // 広告枠の定義
      window.alm.que.push(function () {
        window.alm.defineAdSlot("/1/rewarded", "reward-ad-container");

        // イベントリスナーの設定
        window.alm
          .getAdTracker("reward-ad-container")
          .then(function (adTracker) {
            adTracker.track(window.alm.Events.REWARDED, function () {
              alert("おめでとうございます!10ポイント獲得しました!");
            });

            adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
              alert(
                "申し訳ありません、広告の読み込みに失敗しました。後でもう一度お試しください。"
              );
            });
          });
      });

      // ボタンクリックで広告表示
      document
        .getElementById("show-ad-button")
        .addEventListener("click", function () {
          window.alm.que.push(function () {
            window.alm.render("reward-ad-container");
          });
        });
    </script>
  </body>
</html>

技術的ポイント

  • 事前読み込み: ユーザー体験向上のため、広告は表示前に事前読み込みしておくことをお勧めします
  • エラー処理: 様々な状況(ネットワークエラー、広告在庫切れなど)に対応するため、必ずエラーハンドリングを実装してください
  • 報酬の用意: リワード獲得時は分かりやすい報酬を提供すると、ユーザー満足度を高めることができます

実装例集

実際のユースケースに基づいた具体的な実装例を紹介します。

ケース 1: ゲーム内での動画視聴報酬

ゲーム内でユーザーが動画を視聴することで、ゲーム内通貨やアイテムを獲得できる実装例です。

<!DOCTYPE html>
<html>
  <head>
    <title>ゲーム内リワード広告</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cdn.adlookmanager.com/manager.js" async></script>
    <script>
      window.alm = window.alm || { que: [] };

      // ゲーム内通貨を管理するクラス
      class GameCurrency {
        constructor() {
          this.coins = 0;
        }

        addCoins(amount) {
          this.coins += amount;
          this.updateUI();
        }

        updateUI() {
          document.getElementById("coinBalance").textContent = this.coins;
        }
      }

      const gameCurrency = new GameCurrency();

      // 広告の初期化と設定
      function initializeAd() {
        window.alm.que.push(function () {
          // 広告スロットの定義
          window.alm
            .defineAdSlot("/1234/game_reward", "rewardAdContainer")
            .then(() => {
              console.log("広告の準備完了");
              document.getElementById("watchButton").disabled = false;
            })
            .catch((error) => {
              console.error("広告の初期化に失敗:", error);
            });

          // イベントトラッカーの設定
          window.alm
            .getAdTracker("rewardAdContainer")
            .then(function (adTracker) {
              // リワード付与時の処理
              adTracker.track(window.alm.Events.REWARDED, function () {
                gameCurrency.addCoins(100);
                showMessage("100コインを獲得しました!");
              });

              // エラー時の処理
              adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
                showMessage("申し訳ありません。現在広告を表示できません。");
              });
            });
        });
      }

      // 広告表示ボタンのクリックハンドラ
      function showAd() {
        document.getElementById("watchButton").disabled = true;

        window.alm.que.push(function () {
          window.alm.render("rewardAdContainer").catch((error) => {
            console.error("広告の表示に失敗:", error);
            document.getElementById("watchButton").disabled = false;
          });
        });
      }

      // メッセージ表示用のヘルパー関数
      function showMessage(message) {
        const messageEl = document.getElementById("message");
        messageEl.textContent = message;
        messageEl.style.display = "block";
        setTimeout(() => {
          messageEl.style.display = "none";
        }, 3000);
      }
    </script>
  </head>
  <body onload="initializeAd()">
    <div id="gameContainer">
      <h2>ゲーム内通貨: <span id="coinBalance">0</span>コイン</h2>
      <button id="watchButton" onclick="showAd()" disabled>
        動画を見て100コインをゲット!
      </button>
      <div id="rewardAdContainer"></div>
      <div id="message" style="display: none;"></div>
    </div>
  </body>
</html>

ケース 2: 記事の続きを読むための実装

有料記事の一部を無料で読むために、リワード広告を視聴する実装例です。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>記事閲覧 - リワード広告</title>
    <script src="https://cdn.adlookmanager.com/manager.js" async></script>
    <script>
      window.alm = window.alm || { que: [] };

      class ArticleManager {
        constructor() {
          this.isUnlocked = false;
        }

        unlockArticle() {
          this.isUnlocked = true;
          document.getElementById("articleContent").style.display = "block";
          document.getElementById("adContainer").style.display = "none";
        }
      }

      const articleManager = new ArticleManager();

      function initializeArticleAd() {
        window.alm.que.push(function () {
          // 広告スロットの定義
          window.alm
            .defineAdSlot("/1234/article_unlock", "articleAdSlot")
            .then(() => {
              document.getElementById("unlockButton").disabled = false;
            });

          // イベントトラッカーの設定
          window.alm.getAdTracker("articleAdSlot").then(function (adTracker) {
            // リワード付与時の処理
            adTracker.track(window.alm.Events.REWARDED, function () {
              articleManager.unlockArticle();
              showNotification("記事のロックを解除しました!");
            });

            // エラー時の処理
            adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
              showNotification(
                "エラーが発生しました。しばらく経ってから再度お試しください。",
                "error"
              );
            });
          });
        });
      }

      function showArticleAd() {
        document.getElementById("unlockButton").disabled = true;

        window.alm.que.push(function () {
          window.alm.render("articleAdSlot").catch(() => {
            document.getElementById("unlockButton").disabled = false;
          });
        });
      }

      function showNotification(message, type = "success") {
        const notification = document.getElementById("notification");
        notification.textContent = message;
        notification.className = `notification ${type}`;
        notification.style.display = "block";
        setTimeout(() => {
          notification.style.display = "none";
        }, 3000);
      }
    </script>
    <style>
      .article-preview {
        margin-bottom: 20px;
        padding: 20px;
        background: #f5f5f5;
      }

      .content-locked {
        display: none;
        padding: 20px;
      }

      .notification {
        display: none;
        padding: 10px;
        margin: 10px 0;
        border-radius: 4px;
      }

      .notification.success {
        background: #d4edda;
        color: #155724;
      }

      .notification.error {
        background: #f8d7da;
        color: #721c24;
      }
    </style>
  </head>
  <body onload="initializeArticleAd()">
    <article>
      <h1>記事タイトル</h1>

      <div class="article-preview">
        <p>記事のプレビュー部分です...</p>
      </div>

      <div id="adContainer">
        <button id="unlockButton" onclick="showArticleAd()" disabled>
          動画を見て続きを読む
        </button>
        <div id="articleAdSlot"></div>
      </div>

      <div id="articleContent" class="content-locked">
        <p>記事の続きの内容がここに表示されます...</p>
      </div>

      <div id="notification"></div>
    </article>
  </body>
</html>

ケース 3: アプリ内課金の代替として

アプリ内で特別なコンテンツやアイテムをアンロックするための代替手段として、リワード広告を実装する例です。

<!DOCTYPE html>
<html>
  <head>
    <title>プレミアムコンテンツ - リワード広告</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cdn.adlookmanager.com/manager.js" async></script>
    <script>
      window.alm = window.alm || { que: [] };

      class ContentManager {
        constructor() {
          this.unlockedContent = new Set();
        }

        unlockContent(contentId) {
          this.unlockedContent.add(contentId);
          this.updateContentDisplay(contentId);
          this.saveProgress();
        }

        updateContentDisplay(contentId) {
          const contentElement = document.getElementById(
            `content-${contentId}`
          );
          const unlockButton = document.getElementById(`unlock-${contentId}`);

          if (contentElement && unlockButton) {
            contentElement.style.display = "block";
            unlockButton.parentElement.style.display = "none";
          }
        }

        saveProgress() {
          localStorage.setItem(
            "unlockedContent",
            JSON.stringify([...this.unlockedContent])
          );
        }

        loadProgress() {
          const saved = localStorage.getItem("unlockedContent");
          if (saved) {
            this.unlockedContent = new Set(JSON.parse(saved));
            this.unlockedContent.forEach((contentId) => {
              this.updateContentDisplay(contentId);
            });
          }
        }
      }

      const contentManager = new ContentManager();

      function initializeContentAd(contentId) {
        window.alm.que.push(function () {
          const adSlotId = `adSlot-${contentId}`;

          window.alm
            .defineAdSlot("/1234/premium_content", adSlotId)
            .then(() => {
              document.getElementById(`unlock-${contentId}`).disabled = false;
            });

          window.alm.getAdTracker(adSlotId).then(function (adTracker) {
            adTracker.track(window.alm.Events.REWARDED, function () {
              contentManager.unlockContent(contentId);
              showToast("コンテンツをアンロックしました!");
            });

            adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
              showToast(
                "エラーが発生しました。後でもう一度お試しください。",
                "error"
              );
            });
          });
        });
      }

      function showContentAd(contentId) {
        const button = document.getElementById(`unlock-${contentId}`);
        button.disabled = true;

        window.alm.que.push(function () {
          window.alm.render(`adSlot-${contentId}`).catch(() => {
            button.disabled = false;
          });
        });
      }

      function showToast(message, type = "success") {
        const toast = document.createElement("div");
        toast.className = `toast ${type}`;
        toast.textContent = message;
        document.body.appendChild(toast);

        setTimeout(() => {
          toast.remove();
        }, 3000);
      }
    </script>
    <style>
      .premium-content {
        display: none;
        padding: 20px;
        margin: 10px 0;
        background: #f8f9fa;
        border-radius: 4px;
      }

      .unlock-section {
        padding: 20px;
        margin: 10px 0;
        background: #e9ecef;
        border-radius: 4px;
        text-align: center;
      }

      .toast {
        position: fixed;
        bottom: 20px;
        right: 20px;
        padding: 10px 20px;
        border-radius: 4px;
        color: white;
        animation: fadeIn 0.3s, fadeOut 0.3s 2.7s;
      }

      .toast.success {
        background: #28a745;
      }

      .toast.error {
        background: #dc3545;
      }

      @keyframes fadeIn {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }

      @keyframes fadeOut {
        from {
          opacity: 1;
        }
        to {
          opacity: 0;
        }
      }
    </style>
  </head>
  <body onload="contentManager.loadProgress()">
    <div class="content-container">
      <!-- プレミアムコンテンツ1 -->
      <div class="unlock-section" id="unlock-section-1">
        <h3>プレミアムスキン</h3>
        <button id="unlock-1" onclick="showContentAd('1')" disabled>
          動画を見てアンロック
        </button>
        <div id="adSlot-1"></div>
      </div>
      <div id="content-1" class="premium-content">
        <h3>プレミアムスキンをアンロックしました!</h3>
        <p>新しいスキンを使用できます。</p>
      </div>

      <!-- プレミアムコンテンツ2 -->
      <div class="unlock-section" id="unlock-section-2">
        <h3>特別なアイテム</h3>
        <button id="unlock-2" onclick="showContentAd('2')" disabled>
          動画を見てアンロック
        </button>
        <div id="adSlot-2"></div>
      </div>
      <div id="content-2" class="premium-content">
        <h3>特別なアイテムをアンロックしました!</h3>
        <p>新しいアイテムを使用できます。</p>
      </div>
    </div>

    <script>
      // 各コンテンツの広告を初期化
      initializeContentAd("1");
      initializeContentAd("2");
    </script>
  </body>
</html>

ケース 4: 1 日 1 回限定のデイリーボーナス

1 日 1 回だけリワード広告を視聴できる実装例です。LocalStorage を使用して最後に視聴した日時を管理します。

<!DOCTYPE html>
<html>
  <head>
    <title>デイリーボーナス - リワード広告</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cdn.adlookmanager.com/manager.js" async></script>
    <script>
      window.alm = window.alm || { que: [] };

      class DailyRewardManager {
        constructor() {
          this.lastRewardTime = this.loadLastRewardTime();
        }

        loadLastRewardTime() {
          const saved = localStorage.getItem("lastRewardTime");
          return saved ? new Date(saved) : null;
        }

        saveLastRewardTime() {
          const now = new Date();
          localStorage.setItem("lastRewardTime", now.toISOString());
          this.lastRewardTime = now;
        }

        canShowReward() {
          if (!this.lastRewardTime) {
            return true;
          }

          const now = new Date();
          const lastReward = new Date(this.lastRewardTime);

          // 日付が変わっているかチェック
          return now.toDateString() !== lastReward.toDateString();
        }

        getNextRewardTime() {
          if (!this.lastRewardTime) {
            return new Date();
          }

          const nextReward = new Date(this.lastRewardTime);
          nextReward.setDate(nextReward.getDate() + 1);
          nextReward.setHours(0, 0, 0, 0);
          return nextReward;
        }

        formatTimeUntilNext() {
          const now = new Date();
          const next = this.getNextRewardTime();
          const diff = next - now;

          const hours = Math.floor(diff / (1000 * 60 * 60));
          const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));

          return `${hours}時間${minutes}`;
        }
      }

      const rewardManager = new DailyRewardManager();

      function initializeDailyAd() {
        window.alm.que.push(function () {
          // 広告スロットの定義
          window.alm
            .defineAdSlot("/1234/daily_reward", "dailyAdSlot")
            .then(() => {
              updateRewardButton();
            });

          // イベントトラッカーの設定
          window.alm.getAdTracker("dailyAdSlot").then(function (adTracker) {
            // リワード付与時の処理
            adTracker.track(window.alm.Events.REWARDED, function () {
              rewardManager.saveLastRewardTime();
              addDailyBonus(100);
              showNotification("デイリーボーナスを獲得しました!");
              updateRewardButton();
            });

            // エラー時の処理
            adTracker.track(window.alm.Events.AD_LOADING_FAILED, function () {
              showNotification("エラーが発生しました。", "error");
              updateRewardButton();
            });
          });
        });
      }

      function showDailyAd() {
        if (!rewardManager.canShowReward()) {
          showNotification("本日の報酬は既に受け取り済みです。", "info");
          return;
        }

        const button = document.getElementById("rewardButton");
        button.disabled = true;

        window.alm.que.push(function () {
          window.alm.render("dailyAdSlot").catch(() => {
            button.disabled = false;
          });
        });
      }

      function updateRewardButton() {
        const button = document.getElementById("rewardButton");
        const statusText = document.getElementById("rewardStatus");

        if (rewardManager.canShowReward()) {
          button.disabled = false;
          button.textContent = "デイリーボーナスを受け取る";
          statusText.textContent = "報酬を受け取れます!";
          statusText.className = "status-available";
        } else {
          button.disabled = true;
          button.textContent = "明日までお待ちください";
          statusText.textContent = `次の報酬まで: ${rewardManager.formatTimeUntilNext()}`;
          statusText.className = "status-waiting";
        }
      }

      function addDailyBonus(amount) {
        const currentPoints = parseInt(localStorage.getItem("points") || "0");
        const newPoints = currentPoints + amount;
        localStorage.setItem("points", newPoints);
        document.getElementById("pointsDisplay").textContent = newPoints;
      }

      function showNotification(message, type = "success") {
        const notification = document.getElementById("notification");
        notification.textContent = message;
        notification.className = `notification ${type}`;
        notification.style.display = "block";

        setTimeout(() => {
          notification.style.display = "none";
        }, 3000);
      }

      // 定期的に状態を更新
      setInterval(updateRewardButton, 60000); // 1分ごとに更新
    </script>
    <style>
      .container {
        max-width: 600px;
        margin: 0 auto;
        padding: 20px;
        text-align: center;
      }

      .points {
        font-size: 24px;
        margin: 20px 0;
      }

      .reward-status {
        margin: 15px 0;
        padding: 10px;
        border-radius: 4px;
      }

      .status-available {
        color: #28a745;
        background-color: #d4edda;
      }

      .status-waiting {
        color: #6c757d;
        background-color: #e9ecef;
      }

      .notification {
        display: none;
        padding: 10px;
        margin: 10px 0;
        border-radius: 4px;
      }

      .notification.success {
        background: #d4edda;
        color: #155724;
      }

      .notification.error {
        background: #f8d7da;
        color: #721c24;
      }

      .notification.info {
        background: #d1ecf1;
        color: #0c5460;
      }

      button {
        padding: 10px 20px;
        font-size: 16px;
        border-radius: 4px;
        border: none;
        background-color: #007bff;
        color: white;
        cursor: pointer;
      }

      button:disabled {
        background-color: #6c757d;
        cursor: not-allowed;
      }
    </style>
  </head>
  <body onload="initializeDailyAd()">
    <div class="container">
      <h1>デイリーボーナス</h1>

      <div class="points">
        現在のポイント: <span id="pointsDisplay">0</span>
      </div>

      <div id="rewardStatus" class="reward-status"></div>

      <button id="rewardButton" onclick="showDailyAd()" disabled>
        読み込み中...
      </button>

      <div id="dailyAdSlot"></div>
      <div id="notification"></div>
    </div>
  </body>
</html>