跳到主內容

為您的移動遊戲新增成就和排行榜

如何使用 `games_services` 外掛向您的遊戲新增功能。

玩家玩遊戲有各種各樣的動機。從廣義上講,有四種主要的動機:沉浸感、成就感、合作和競爭。無論您構建什麼遊戲,有些玩家都想在其中取得成就。這可能是贏得獎盃或解鎖秘密。有些玩家想在其中競爭。這可能是獲得高分或完成加速通關。這兩個想法對應於成就排行榜的概念。

A simple graphic representing the four types of motivation explained above

App Store 和 Google Play 等生態系統為成就和排行榜提供集中的服務。玩家可以在一個地方檢視所有遊戲的成就,而開發者無需為每個遊戲重新實現它們。

本教程演示瞭如何使用 games_services將成就和排行榜功能新增到您的手遊中。

1. 啟用平臺服務

#

要啟用遊戲服務,請在 iOS 上設定 Game Center,並在 Android 上設定 Google Play 遊戲服務

iOS

#

要在 iOS 上啟用 Game Center (GameKit)

  1. 在 Xcode 中開啟您的 Flutter 專案。開啟 ios/Runner.xcworkspace

  2. 選擇根 Runner 專案。

  3. 轉到 Signing & Capabilities 選項卡。

  4. 單擊 + 按鈕將 Game Center 新增為功能。

  5. 關閉 Xcode。

  6. 如果您尚未這樣做,請在 App Store Connect 中註冊您的遊戲,並從 My App 部分按 + 圖示。

    Screenshot of the + button in App Store Connect

  7. 仍然在 App Store Connect 中,查詢 Game Center 部分。撰寫本文時,您可以在 Services 中找到它。在 Game Center 頁面上,您可能需要根據您的遊戲設定排行榜和幾個成就。請記下您建立的排行榜和成就的 ID。

Android

#

要啟用 Android 上的 Play 遊戲服務

  1. 如果您尚未這樣做,請轉到 Google Play Console 並在此處註冊您的遊戲。

    Screenshot of the 'Create app' button in Google Play Console

  2. 仍然在 Google Play Console 中,從導航選單中選擇 Play 遊戲服務設定和管理配置,並按照他們的說明操作。

    • 這需要花費大量時間和耐心。在其他事項中,您需要在 Google Cloud Console 中設定 OAuth 同意螢幕。如果您在任何時候感到迷茫,請參閱官方 Play 遊戲服務指南

      Screenshot showing the Games Services section in Google Play Console

  3. 完成操作後,您可以開始在 Play 遊戲服務設定和管理 中新增排行榜和成就。建立與 iOS 端完全相同的一組。記下 ID。

  4. 轉到 Play 遊戲服務 → 設定和管理 → 釋出

  5. 單擊 釋出。不用擔心,這實際上不會發布您的遊戲。它只會釋出成就和排行榜。例如,一旦排行榜以這種方式釋出,就無法取消釋出。

  6. 轉到 Play 遊戲服務 → 設定和管理 → 配置 → 憑據

  7. 找到 獲取資源 按鈕。它返回一個包含 Play 遊戲服務 ID 的 XML 檔案。

    xml
    <!-- THIS IS JUST AN EXAMPLE -->
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <!--app_id-->
        <string name="app_id" translatable="false">424242424242</string>
        <!--package_name-->
        <string name="package_name" translatable="false">dev.flutter.tictactoe</string>
        <!--achievement First win-->
        <string name="achievement_first_win" translatable="false">sOmEiDsTrInG</string>
        <!--leaderboard Highest Score-->
        <string name="leaderboard_highest_score" translatable="false">sOmEiDsTrInG</string>
    </resources>
    
  8. android/app/src/main/res/values/games-ids.xml 中新增一個檔案,其中包含您在上一步驟中收到的 XML。

2. 登入遊戲服務

#

現在您已經設定了 Game CenterPlay 遊戲服務,並且已經準備好了成就和排行榜 ID,終於可以開始使用 Dart 了。

  1. 新增對 games_services的依賴項。

    flutter pub add games_services
    
  2. 在您執行任何操作之前,您必須將玩家登入到遊戲服務。

    dart
    try {
      await GamesServices.signIn();
    } on PlatformException catch (e) {
      // ... deal with failures ...
    }
    

登入發生在後臺。這需要幾秒鐘,因此不要在 runApp() 之前呼叫 signIn(),否則玩家每次啟動遊戲時都會被迫盯著空白螢幕。

games_services API 的 API 呼叫可能由於多種原因而失敗。因此,每個呼叫都應包裝在 try-catch 塊中,如前一個示例所示。為了清晰起見,本教程的其餘部分省略了異常處理。

3. 解鎖成就

#
  1. 在 Google Play Console 和 App Store Connect 中註冊成就,並記下它們的 ID。現在您可以從您的 Dart 程式碼中授予任何這些成就

    dart
    await GamesServices.unlock(
      achievement: Achievement(
        androidID: 'your android id',
        iOSID: 'your ios id',
      ),
    );
    

    玩家在 Google Play 遊戲或 Apple Game Center 上的帳戶現在列出了該成就。

  2. 要從您的遊戲中顯示成就 UI,請呼叫 games_services API

    dart
    await GamesServices.showAchievements();
    

    這會將平臺成就 UI 作為疊加層顯示在您的遊戲上。

  3. 要以您自己的 UI 顯示成就,請使用 GamesServices.loadAchievements()

4. 提交分數

#

當玩家完成遊戲時,您的遊戲可以將該遊戲會話的結果提交到一個或多個排行榜。

例如,像超級馬里奧這樣的平臺遊戲可以將最終得分和完成關卡所用的時間提交到兩個單獨的排行榜。

  1. 在第一步中,您在 Google Play Console 和 App Store Connect 中註冊了一個排行榜,並記下了它的 ID。使用此 ID,您可以為玩家提交新的分數

    dart
    await GamesServices.submitScore(
      score: Score(
        iOSLeaderboardID: 'some_id_from_app_store',
        androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',
        value: 100,
      ),
    );
    

    您無需檢查新的分數是否是玩家的最高分。平臺遊戲服務會為您處理。

  2. 要將排行榜作為遊戲的疊加層顯示,請進行以下呼叫

    dart
    await GamesServices.showLeaderboards(
      iOSLeaderboardID: 'some_id_from_app_store',
      androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',
    );
    
  3. 如果您想以您自己的 UI 顯示排行榜分數,可以使用 GamesServices.loadLeaderboardScores() 獲取它們。

5. 後續步驟

#

games_services 外掛還有更多功能。使用此外掛,您可以

  • 獲取玩家的圖示、姓名或唯一 ID
  • 儲存和載入遊戲狀態
  • 退出遊戲服務

有些成就是增量的。例如:“您已收集了所有 10 個麥高芬碎片。”

每個遊戲對遊戲服務都有不同的需求。

首先,您可能想建立此控制器,以便將所有成就和排行榜邏輯放在一個地方

dart
import 'dart:async';

import 'package:games_services/games_services.dart';
import 'package:logging/logging.dart';

/// Allows awarding achievements and leaderboard scores,
/// and also showing the platforms' UI overlays for achievements
/// and leaderboards.
///
/// A facade of `package:games_services`.
class GamesServicesController {
  static final Logger _log = Logger('GamesServicesController');

  final Completer<bool> _signedInCompleter = Completer();

  Future<bool> get signedIn => _signedInCompleter.future;

  /// Unlocks an achievement on Game Center / Play Games.
  ///
  /// You must provide the achievement ids via the [iOS] and [android]
  /// parameters.
  ///
  /// Does nothing when the game isn't signed into the underlying
  /// games service.
  Future<void> awardAchievement({
    required String iOS,
    required String android,
  }) async {
    if (!await signedIn) {
      _log.warning('Trying to award achievement when not logged in.');
      return;
    }

    try {
      await GamesServices.unlock(
        achievement: Achievement(androidID: android, iOSID: iOS),
      );
    } catch (e) {
      _log.severe('Cannot award achievement: $e');
    }
  }

  /// Signs into the underlying games service.
  Future<void> initialize() async {
    try {
      await GamesServices.signIn();
      // The API is unclear so we're checking to be sure. The above call
      // returns a String, not a boolean, and there's no documentation
      // as to whether every non-error result means we're safely signed in.
      final signedIn = await GamesServices.isSignedIn;
      _signedInCompleter.complete(signedIn);
    } catch (e) {
      _log.severe('Cannot log into GamesServices: $e');
      _signedInCompleter.complete(false);
    }
  }

  /// Launches the platform's UI overlay with achievements.
  Future<void> showAchievements() async {
    if (!await signedIn) {
      _log.severe('Trying to show achievements when not logged in.');
      return;
    }

    try {
      await GamesServices.showAchievements();
    } catch (e) {
      _log.severe('Cannot show achievements: $e');
    }
  }

  /// Launches the platform's UI overlay with leaderboard(s).
  Future<void> showLeaderboard() async {
    if (!await signedIn) {
      _log.severe('Trying to show leaderboard when not logged in.');
      return;
    }

    try {
      await GamesServices.showLeaderboards(
        // TODO: When ready, change both these leaderboard IDs.
        iOSLeaderboardID: 'some_id_from_app_store',
        androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',
      );
    } catch (e) {
      _log.severe('Cannot show leaderboard: $e');
    }
  }

  /// Submits [score] to the leaderboard.
  Future<void> submitLeaderboardScore(int score) async {
    if (!await signedIn) {
      _log.warning('Trying to submit leaderboard when not logged in.');
      return;
    }

    _log.info('Submitting $score to leaderboard.');

    try {
      await GamesServices.submitScore(
        score: Score(
          // TODO: When ready, change these leaderboard IDs.
          iOSLeaderboardID: 'some_id_from_app_store',
          androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',
          value: score,
        ),
      );
    } catch (e) {
      _log.severe('Cannot submit leaderboard score: $e');
    }
  }
}

更多資訊

#

Flutter Casual Games Toolkit 包含以下模板

  • basic:基本的啟動遊戲
  • card:啟動紙牌遊戲
  • endless runner:啟動遊戲(使用 Flame),玩家無休止地奔跑,躲避陷阱並獲得獎勵