導入範例

Ad Placement API 的設計宗旨,是協助 AdSense 和 AdMob 開發人員在網頁或應用程式的 HTML5 遊戲中,使用中插和獎勵廣告。這個範例示範如何將 Ad Placement API 整合到遊戲中,並使用該 API 放置插頁式廣告。

必要條件

開始之前,請先備妥下列項目:

  • 在同一個目錄中建立兩個空白檔案:
    • index.html
    • game.js
  • 在本機安裝 Python,或使用網路伺服器測試實作項目

應用程式程式碼範例

AdMob 發布商可以下載範例應用程式程式碼,進一步瞭解如何將 API 整合至應用程式遊戲。

下載應用程式範例程式碼

1. 啟動開發伺服器

由於 Ads Placement API 會透過與載入網頁相同的通訊協定載入依附元件,因此您需要使用網路伺服器測試應用程式。您可以使用 Python 內建的伺服器建立本機開發環境。

  1. 開啟終端機。

  2. 前往含有 index.html 檔案的目錄,然後執行:

    python -m http.server 8000
    
  3. 使用網路瀏覽器前往 localhost:8000

您也可以使用任何其他網頁伺服器,例如 Apache HTTP 伺服器

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 內的其他網頁,且遊戲的 HTML 網頁中含有 adsbygoogle 標記,請務必將 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 Mobile Ads SDK (如有) 通訊,要求並顯示 AdMob 廣告。

這項功能適用於已連結 Google Mobile Ads SDK 的 Android 應用程式。如要啟用這項功能,您需要註冊 WebView,透過 Google Mobile Ads SDK 顯示遊戲,然後設定 AdMob 廣告單元,並將這些單元做為額外參數傳遞至 AdSense 代碼。當遊戲在合適的應用程式中執行時,廣告刊登位置 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 代碼參數,表明遊戲應只顯示 AdMob 廣告,且在遊戲於不支援 AdMob 請求的環境 (例如非應用程式環境或未設定 Google Mobile Ads SDK 的應用程式) 中執行時,不應改用 AdSense。

重要事項:如果您將遊戲設計為嵌入應用程式,且本身就是應用程式的擁有者,或即將與應用程式擁有者簽訂收益分潤協議,但希望成效良好且符合政策規定,唯一的方法就是使用這項 AdMob 支援服務。

首先,請使用 Google Mobile Ads SDK 註冊將顯示遊戲的 WebView

MainActivity.java (應用程式)

預設 WebView 設定並未針對廣告進行最佳化。使用 WebSettings API 設定 WebView,包括:

  • JavaScript
  • 存取本機儲存空間
  • 自動播放影片

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 接著會使用這項資訊,篩選要求放送的廣告類型,確保廣告適合遊戲 (例如,如果已啟用音效,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() 呼叫會定義廣告刊登位置,並採用名為刊登位置設定的物件,指定在遊戲中這個時間點顯示廣告所需的一切項目。如要支援不同類型的廣告,您需要初始化刊登位置設定的不同子集。

adBreak() 呼叫會定義廣告可顯示的刊登位置,換句話說,就是顯示廣告的機會。廣告實際上是否會顯示取決於下列因素:

  • 您聲明的廣告刊登位置類型。
  • 如果在此廣告刊登位置之前,使用者已進行適當的互動。
  • 目前播放器是否有合適的廣告,且:
    • 與他們相關。
    • 與資料隱私權和同意聲明設定一致。
  • 使用者最近看過的廣告數量。
  • 您為這款遊戲設定的控制項,包括:
    • 代碼中的提示。
    • 在 AdSense 中 (注意:AdSense 提供的控制選項會隨時間演進)

新增程式碼,在遊戲重新啟動時顯示插頁式廣告:在 play() 函式中呼叫 adBreak(),這個函式只會在遊戲完整執行一次後執行。

adBreak() 必須在使用者操作時呼叫,例如點選「播放」按鈕,否則 API 無法要求及顯示廣告。

建立要在廣告插播前後呼叫的函式,然後在 adBreak() 刊登位置設定中使用這些函式。請注意,只有在找到合適的廣告時,系統才會呼叫 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() 刊登位置設定中使用這些函式。請注意,只有在找到合適的廣告時,才會呼叫 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" 行,因為這段程式碼會啟用正式版中的測試設定。