实现示例

Ad Placement API 旨在帮助 AdSense 和 AdMob 开发者在网站上或应用内的 HTML5 游戏中使用插页式广告和激励广告。此示例演示了如何将 Ad Placement API 集成到游戏中,以及如何使用它投放插页式广告。

前提条件

在开始之前,您需要完成以下操作:

  • 在同一目录中创建两个空白文件:
    • index.html
    • game.js
  • 在本地安装 Python,或使用网络服务器来测试实现情况

应用示例代码

AdMob 发布商可以下载示例应用代码,以更好地了解如何将该 API 集成到应用游戏中。

下载应用示例代码

1. 启动开发服务器

Ads Placement API 加载依赖项时会使用协议,此协议与加载该 API 的网页所用的协议相同,因此您需要使用网络服务器来测试应用。您可以使用 Python 的内置服务器来创建本地开发环境。

  1. 打开终端。

  2. 前往包含 index.html 文件的目录,然后运行以下命令:

    python -m http.server 8000
    
  3. 在网络浏览器中,前往 localhost:8000

您也可以使用任何其他网络服务器,例如 Apache HTTP Server

2. 制作 HTML5 游戏

修改 index.html,在其中创建一个 HTML5 画布元素以及一个用于触发游戏的按钮。然后,添加必要的脚本标记来加载 game.js 文件。

index.html

<!doctype html>
<html lang="en">
  <head>
    <title>Ad Placement API HTML5 demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <canvas id="gameContainer" height="300px" width="300px"></canvas>
    <br>
    <button id="playButton">Play</button>
    <button style="display:none" id="headsButton">Heads</button>
    <button style="display:none" id="tailsButton">Tails</button>

    <script src="game.js"></script>
  </body>
</html>

修改 game.js,让用户在点击“Play”(开始游戏)按钮后即可玩抛硬币游戏。

game.js

// Create a coin flip game
class Game {
  constructor() {
    // Define variables
    this.score = 0;
    this.choice = '';

    this.canvas = document.getElementById('gameContainer').getContext('2d');
    this.canvas.font = '24px Arial';

    this.playButton = document.getElementById('playButton');
    this.headsButton = document.getElementById('headsButton');
    this.tailsButton = document.getElementById('tailsButton');

    // On click listeners for the game's buttons.
    this.playButton.addEventListener('click', () => {
      this.erase();
      this.play();
    });

    this.headsButton.addEventListener('click', () => {
      this.choice = 'Heads';
      this.flipCoin();
    });

    this.tailsButton.addEventListener('click', () => {
      this.choice = 'Tails';
      this.flipCoin();
    });

    this.erase();
  }

  // Start the game
  play() {
    this.score = 0;
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('Heads or Tails?', 66, 150);
    this.playButton.style.display = 'none';
    this.headsButton.style.display = 'inline-block';
    this.tailsButton.style.display = 'inline-block';
  }

  // Flip the coin
  flipCoin() {
    this.headsButton.disabled = true;
    this.tailsButton.disabled = true;
    this.erase();
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('Flipping coin . . .', 60, 150);

    setTimeout(() => { this.coinLanded() }, 2000);
  }

  // Logic for when the coin lands
  coinLanded() {
    this.headsButton.disabled = false;
    this.tailsButton.disabled = false;
    let sideUp;
    if(Math.random() < 0.5) {
      sideUp = 'Heads';
    } else {
      sideUp = 'Tails';
    }

    if (sideUp === this.choice ) {
      this.win(sideUp);
    } else {
      this.lose(sideUp);
    }
  }

  // Guess the flip correctly
  win(sideUp) {
    this.erase();
    this.score += 1;
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('It was ' + sideUp + '!', 66, 150);
    this.canvas.fillText('Guess again', 70, 200);
  }

  // Guess the flip incorrectly
  lose(sideUp) {
    this.erase();
    this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
    this.canvas.fillText('Your score was ' + this.score, 50, 150);
    this.canvas.fillText('Want to play again?', 45, 200);

    this.playButton.style.display = 'inline-block';
    this.headsButton.style.display = 'none';
    this.tailsButton.style.display = 'none';
  }

  // Erase the canvas
  erase() {
    this.canvas.fillStyle = '#ADD8E6';
    this.canvas.fillRect(0, 0, 300, 300);
    this.canvas.fillStyle = '#000000';
  }
}

const game = new Game();

完成此步骤后,当您在浏览器中(通过开发服务器)打开 index.html 时,应该可以看到游戏画布和“Play”(开始游戏)按钮。如果您点击“Play”(开始游戏),应该就能开始玩抛硬币游戏。

3. 导入 Ad Placement API

接下来,在 index.html 中插入一个脚本标记,将其放在 game.js 对应的标记之前,以便将 Ad Placement API 添加到游戏中。

此脚本标记可接受多种参数。我们将使用以下参数指定 AdSense 媒体资源代码并启用测试模式:

  • data-ad-client=<AdSense property code> 您的 AdSense 媒体资源代码。即使是要在应用内运行的游戏,也始终需要提供该代码。
  • data-adbreak-test="on" 启用测试模式。游戏正式向玩家开放后,请移除此参数。

设置 AdSense 代码并开启测试模式

AdSense 代码包含 Ad Placement API 功能。要开启此功能,您必须先添加 AdSense 代码,并添加一小段脚本代码段,用于初始化它的两大主要函数:adBreak()adConfig()

index.html(网站)

 [...]
    <canvas id="gameContainer" height="300px" width="300px"></canvas>
    <br>
    <button id="playButton">Play</button>
    <button style="display:none" id="headsButton">Heads</button>
    <button style="display:none" id="tailsButton">Tails</button>

    <script async
      data-adbreak-test="on"
      src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
      crossorigin="anonymous">
    </script>
    <script>
      window.adsbygoogle = window.adsbygoogle || [];
      const adBreak = adConfig = function(o) {adsbygoogle.push(o);}
    </script>
    <script src="game.js"></script>
  </body>
</html>

嵌入您的游戏(可选)

如果您希望利用 iframe 在其他页面中嵌入游戏,且 adsbygoogle 标记位于该游戏的 HTML 页面中,请务必将 allow='autoplay' 添加到 iframe 元素中。这是最佳实践,也是确保某些广告有资格在您的游戏中展示所必需的。

<head>
  <!-- The adsbygoogle tag is not here -->
</head>
<body>
  <iframe src="https://www.my-game.com" title="My game" allow="autoplay">
    <!-- The game is loaded here and contains the adsbygoogle tag -->
  </iframe>
</body>

支持移动应用

H5 游戏可以在常规网络浏览器、WebView 或应用内的 Chrome 自定义标签页中运行。Ad Placement API 可以检测游戏运行环境,并据此定向广告请求。如果您的游戏在常规网络浏览器中运行,那么广告请求的处理方式与常规 AdSense 请求类似。如果 Ad Placement API 检测到应用内环境,则会与 Google 移动广告 SDK(如果存在)通信,以请求并展示 AdMob 广告。

已经与 Google 移动广告 SDK 相关联的 Android 应用支持此功能。为了启用此功能,您需要使用 Google 移动广告 SDK 注册将会显示游戏的 WebView,然后配置 AdMob 广告单元,将其作为额外参数传递给 AdSense 代码。当您的游戏在合适的应用内运行时,Ad Placement API 将使用这些广告单元来展示广告。

要启用移动支持,您必须指定下面这些额外的标记参数:

  • data-admob-interstitial-slot=<AdMob slot ID> 您之前配置的 AdMob 插页式广告单元 ID。
  • data-admob-rewarded-slot=<AdMob slot ID> AdMob 激励广告单元 ID。

您的 AdSense 媒体资源代码应始终通过 data-ad-client 参数传递,并且必须至少指定 data-admob-interstitial-slotdata-admob-rewarded-slot 中的一个。如果您的游戏同时使用这两种广告格式,则这两个参数都应指定。

或者,您也可以指定 data-admob-ads-only=on 标记参数,以指示在游戏运行环境(例如非应用环境或未配置 Google 移动广告 SDK 的应用)不支持 AdMob 请求的情况下,您的游戏仅应展示来自 AdMob 的广告,即使没有 AdMob 广告,也不应退而求其次展示 AdSense 广告。

重要提示:如果您想将游戏嵌入到应用中,并且您是该应用的所有者或者与应用所有者签订了收益分成协议,那么您只有使用这一 AdMob 支持,才能做到既获得出色效果,又符合相关政策。

首先,使用 Google 移动广告 SDK 注册将会显示游戏的 WebView

MainActivity.java(应用)

Default WebView settings are not optimized for ads. Use the WebSettings APIs to configure your WebView for:

  • JavaScript
  • Access to local storage
  • Automatic video play

Java

import android.webkit.CookieManager;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity {
  private WebView webView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    webView = findViewById(R.id.webview);

    // Let the web view accept third-party cookies.
    CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
    // Let the web view use JavaScript.
    webView.getSettings().setJavaScriptEnabled(true);
    // Let the web view access local storage.
    webView.getSettings().setDomStorageEnabled(true);
    // Let HTML videos play automatically.
    webView.getSettings().setMediaPlaybackRequiresUserGesture(false);

    // Set the H5AdsWebViewClient.
    h5AdsWebViewClient = new H5AdsWebViewClient(this, webView);
    webView.setWebViewClient(h5AdsWebViewClient);
    h5AdsWebViewClient.setDelegateWebViewClient(pubWebViewClient);

    // Register the web view.
    MobileAds.registerWebView(webView);
  }
}

Kotlin

import android.webkit.CookieManager
import android.webkit.WebView

class MainActivity : AppCompatActivity() {
  lateinit var webView: WebView

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    webView = findViewById(R.id.webview)

    // Let the web view accept third-party cookies.
    CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
    // Let the web view use JavaScript.
    webView.settings.javaScriptEnabled = true
    // Let the web view access local storage.
    webView.settings.domStorageEnabled = true
    // Let HTML videos play automatically.
    webView.settings.mediaPlaybackRequiresUserGesture = false

    // Set the H5AdsWebViewClient.
    val h5AdsWebViewClient = H5AdsWebViewClient(this, webView)
    webView.webViewClient = h5AdsWebViewClient
    h5AdsWebViewClient.delegateWebViewClient = pubWebViewClient

    // Register the web view.
    MobileAds.registerWebView(webView)
  }
}

接下来,按如下所示传递 AdMob 广告单元(一个用于插页式广告,一个用于激励广告):

index.html(应用)

 [...]
    <canvas id="gameContainer" height="300px" width="300px"></canvas>
    <br>
    <button id="playButton">Play</button>
    <button style="display:none" id="headsButton">Heads</button>
    <button style="display:none" id="tailsButton">Tails</button>
    <script async
      data-admob-interstitial-slot="ca-app-pub-0987654321/1234567890"
      data-admob-rewarded-slot="ca-app-pub-0987654321/0987654321"
      src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
      crossorigin="anonymous">
    </script>
    <script>
      window.adsbygoogle = window.adsbygoogle || [];
      const adBreak = adConfig = function(o) {adsbygoogle.push(o);}
    </script>
    <script src="game.js"></script>
  </body>
</html>

4. 调用 adConfig()

adConfig() 调用会将游戏的当前配置传递给 Ad Placement API。然后,该 API 可以使用这些信息过滤要请求的广告类型,以确保所请求的广告适合游戏(例如,如果游戏开启了声音,则要请求需要声音的视频广告)。

每当此配置更改时(如用户将游戏静音或取消静音),都应调用 adConfig()。在游戏的构造函数中调用 adConfig(),然后添加一个游戏静音/取消静音按钮,这会再一次调用 adConfig()

game.js

class Game {
  constructor() {
    // Define variables
    this.score = 0;
    this.choice = '';
    this.muted = false;

    this.canvas = document.getElementById('gameContainer').getContext('2d');
    this.canvas.font = '24px Arial';

    this.playButton = document.getElementById('playButton');
    this.headsButton = document.getElementById('headsButton');
    this.tailsButton = document.getElementById('tailsButton');
    this.muteButton = document.getElementById('muteButton');

    adConfig({
      sound: 'on',
    });

    // On click listeners for the game's buttons.
    this.playButton.addEventListener('click', () => {
      this.erase();
      this.play();
    });

    this.headsButton.addEventListener('click', () => {
      this.choice = 'Heads';
      this.flipCoin();
    });

    this.tailsButton.addEventListener('click', () => {
      this.choice = 'Tails';
      this.flipCoin();
    });

    this.muteButton.addEventListener('click', () => {
      var soundString = this.muted ? 'on' : 'off';
      this.muteButton.innerHTML = this.muted ? 'Mute sound' : 'Un-mute sound';
      this.muted = !this.muted;
      adConfig({
        sound: soundString,
      });
    });

    this.erase();
  [...]

现在,将静音按钮添加到您的 HTML 文件中。

index.html

[...]
    <canvas id="gameContainer" height="300px" width="300px"></canvas>
    <br>
    <button id="playButton">Play</button>
    <button style="display:none" id="headsButton">Heads</button>
    <button style="display:none" id="tailsButton">Tails</button>
    <button id="muteButton">Mute sound</button>

    <script async
      data-adbreak-test="on"
      src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
      crossorigin="anonymous">
    </script>
[...]

5. 在游戏结束时调用 adBreak()

adBreak() 调用用于指定广告展示位置,接受一个名为 placement config 的对象,此对象指定了在游戏结束这一刻展示广告所需的所有内容。若要支持不同类型的广告,您需要初始化 placement config 的不同子集。

adBreak() 调用指定了广告可以展示的位置,也就是展示广告的机会。广告是否能真正展示取决于多种因素:

  • 您声明的广告展示位置的类型。
  • 在此广告展示位置之前是否出现过合适的用户互动。
  • 是否存在适合向当前玩家展示的广告,即满足以下条件的广告:
    • 与玩家有关。
    • 符合玩家的数据隐私和意见征求设置
  • 用户近期看到的广告数量。
  • 您为此游戏配置的控制设置,可通过以下任一方法进行控制:
    • 标记中的设置提示。
    • AdSense 中的控件(请注意:AdSense 中的控件会随着时间的推移而变化)。

添加代码,使插页式广告在游戏重新开始时展示:在 play() 函数内调用 adBreak(),该函数仅在玩家玩完一次游戏后运行。

系统 adBreak() 必须在用户执行操作(如点击“Play”[开始游戏] 按钮)的过程中调用,否则该 API 将无法请求和展示广告。

创建要在广告插播时间点之前和之后调用的函数,您后续需要在 adBreak() placement config 中使用这些函数。请务必注意,只有在找到合适的广告时,系统才会调用 beforeAdafterAd 函数。

game.js

class Game {
  constructor() {
    // Define variables
    this.score = 0;
    this.choice = '';
    this.muted = false;
    this.shouldShowAdOnPlay = false;

  [...]

  // Start the game
  play() {
    if (this.shouldShowAdOnPlay) {
      this.shouldShowAdOnPlay = false;

      adBreak({
        type: 'next',  // ad shows at start of next level
        name: 'restart-game',
        beforeAd: () => { this.disableButtons(); },  // You may also want to mute the game's sound.
        afterAd: () => { this.enableButtons(); },    // resume the game flow.
      });
    }

    this.score = 0;
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('Heads or Tails?', 66, 150);
    this.playButton.style.display = 'none'
    this.headsButton.style.display = 'inline-block'
    this.tailsButton.style.display = 'inline-block'
  }

  [...]

  // Guess the flip incorrectly
  lose(sideUp) {
    this.erase()
    this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
    this.canvas.fillText('Your score was ' + this.score, 50, 150);
    this.canvas.fillText('Want to play again?', 45, 200);

    this.playButton.style.display = 'inline-block'
    this.headsButton.style.display = 'none'
    this.tailsButton.style.display = 'none'
    this.shouldShowAdOnPlay = true;
  }

  [...]

  // Erase the canvas
  erase() {
    this.canvas.fillStyle = '#ADD8E6';
    this.canvas.fillRect(0, 0, 300, 300);
    this.canvas.fillStyle = '#000000';
  }

  enableButtons() {
    this.playButton.disabled = false;
    this.headsButton.disabled = false;
    this.tailsButton.disabled = false;
  }

  disableButtons() {
    this.playButton.disabled = true;
    this.headsButton.disabled = true;
    this.tailsButton.disabled = true;
  }
}

const game = new Game();

6. 为激励广告调用 adBreak()

添加代码,使激励广告在以下情况下展示:游戏结束,但用户想要恢复自己已有的得分,而不是重新开始游戏。在 lose() 函数内调用 adBreak(),检查是否有激励广告可用。如果有,则向用户显示一条提示,询问对方是否想要奖励(即本例中的恢复得分)。如果用户同意观看广告,则调用对应的 showAdFn()。您可以使用 adViewedadDismissed 回调来配置用户观看/跳过激励广告后的操作。

每次有机会展示激励广告时,都应调用一个新的 adBreak()。这样做可确保在上一个广告过期或不可用的情况下刷新广告。

在用户明确表示想要观看广告时,系统必须调用 showAdFn(),否则广告可能不会显示。

创建要在广告插播时间点之前和之后调用的函数,您后续需要在 adBreak() placement config 中使用这些函数。请务必注意,只有在找到合适的广告时,系统才会调用 beforeRewardadViewedadDismissedbeforeAdafterAd 函数。

game.js

class Game {
  constructor() {
    // Define variables
    this.score = 0;
    this.choice = '';
    this.muted = false;
    this.shouldShowAdOnPlay = false;
    this.showRewardedAdFn = null;

    this.canvas = document.getElementById('gameContainer').getContext('2d');
    this.canvas.font = '24px Arial';

    this.playButton = document.getElementById('playButton');
    this.headsButton = document.getElementById('headsButton');
    this.tailsButton = document.getElementById('tailsButton');
    this.muteButton = document.getElementById('muteButton');
    this.continueButton = document.getElementById('continueButton');

    adConfig({
      sound: 'on',
    });

    // On click listeners for the game's buttons.
    this.playButton.addEventListener('click', () => {
      this.erase();
      this.play();
    });

    this.headsButton.addEventListener('click', () => {
      this.choice = 'Heads';
      this.flipCoin();
    });

    this.tailsButton.addEventListener('click', () => {
      this.choice = 'Tails';
      this.flipCoin();
    });

    this.muteButton.addEventListener('click', () => {
      var soundString = this.muted ? 'on' : 'off';
      this.muteButton.innerHTML = this.muted ? 'Mute sound' : 'Un-mute sound';
      this.muted = !this.muted;
      adConfig({
        sound: soundString,
      });
    });

    this.continueButton.addEventListener('click', () => {
      if (this.showRewardedAdFn) {
        this.showRewardedAdFn();
      }
    });

    this.erase();
  }

  // Start the game
  play() {
    if (this.shouldShowAdOnPlay) {
      this.shouldShowAdOnPlay = false;

      adBreak({
        type: 'next',  // ad shows at start of next level
        name: 'restart-game',
        beforeAd: () => { this.disableButtons(); },  // You may also want to mute the game's sound.
        afterAd: () => { this.enableButtons(); },    // resume the game flow.
      });
    }

    this.score = 0;
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('Heads or Tails?', 66, 150);
    this.playButton.style.display = 'none';
    this.continueButton.style.display = 'none';
    this.headsButton.style.display = 'inline-block';
    this.tailsButton.style.display = 'inline-block';
  }

  [...]

  // Guess the flip incorrectly
  lose(sideUp) {
    this.erase()
    this.canvas.fillText('Sorry, it was ' + sideUp, 50, 100);
    this.canvas.fillText('Your score was ' + this.score, 50, 150);
    this.canvas.fillText('Want to play again?', 45, 200);

    this.playButton.style.display = 'inline-block'
    this.headsButton.style.display = 'none'
    this.tailsButton.style.display = 'none'
    this.shouldShowAdOnPlay = true;

    adBreak({
      type: 'reward',  // rewarded ad
      name: 'reward-continue',
      beforeReward: (showAdFn) => {
        this.showRewardedAdFn = () => { showAdFn(); };
        // Rewarded ad available - prompt user for a rewarded ad
        this.continueButton.style.display = 'inline-block';
      },
      beforeAd: () => { this.disableButtons(); },     // You may also want to mute the game's sound.
      adDismissed: () => {
        this.continueButton.style.display = 'none';   // Hide the reward button and continue lose flow.
      },
      adViewed: () => { this.continueGame(); },       // Reward granted - continue game at current score.
      afterAd: () => { this.enableButtons(); },       // Resume the game flow.
    });
  }

  // Continue gameplay at current score
  continueGame() {
    this.erase();
    this.canvas.fillText('Score: ' + this.score, 8, 26);
    this.canvas.fillText('Heads or Tails?', 66, 150);
    this.playButton.style.display = 'none';
    this.continueButton.style.display = 'none';
    this.headsButton.style.display = 'inline-block';
    this.tailsButton.style.display = 'inline-block';
  }
  [...]
}

const game = new Game();

现在,将“继续”按钮添加到您的 HTML 文件中。

index.html

[...]
    <canvas id="gameContainer" height="300px" width="300px"></canvas>
    <br>
    <button id="playButton">Play</button>
    <button style="display:none" id="headsButton">Heads</button>
    <button style="display:none" id="tailsButton">Tails</button>
    <button style="display:none" id="continueButton">Watch Ad to continue?</button>
    <button id="muteButton">Mute sound</button>

    <script async
      data-adbreak-test="on"
      src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456789"
      crossorigin="anonymous">
    </script>
[...]

现在,抛硬币应用正在创建用于展示广告的广告展示位置。

除了游戏结尾处,您自己的应用可能还有其他适合展示广告的位置。在这些位置调用 adBreak() 应该与本示例类似。

关闭正式版应用的测试

在发布应用之前,请务必移除或注释掉 index.html 中的 data-adbreak-test="on" 行,因为此代码会在正式版中开启测试设置。