跳至主要内容

为您的移动游戏添加成就和排行榜

玩家玩游戏的动机多种多样。概括来说,主要有四种动机:沉浸感、成就感、合作和竞争。无论您构建的是什么游戏,有些玩家都希望在游戏中获得成就。这可能是赢得的奖杯或解锁的秘密。有些玩家希望在游戏中进行竞争。这可能是获得高分或完成速通。这两个想法对应于成就排行榜的概念。

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. 转到签名与功能选项卡。

  4. 点击+按钮添加Game Center作为功能。

  5. 关闭 Xcode。

  6. 如果您尚未注册,请在App Store Connect中注册您的游戏,并在我的 App部分按下+图标。

    Screenshot of the + button in App Store Connect

  7. 仍在 App Store Connect 中,查找Game Center部分。截至撰写本文时,您可以在服务中找到它。在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 休闲游戏工具包包含以下模板

  • basic:基本入门游戏
  • card:入门纸牌游戏
  • endless runner:入门游戏(使用 Flame),玩家可以无限奔跑,躲避陷阱并获得奖励