Skip to main content

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

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

上面解释的四种动机类型的简单图形

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 部分按下 + 图标。

    App Store Connect 中的 + 按钮屏幕截图

  7. 仍在 App Store Connect 中,查找 Game Center 部分。在撰写本文时,您可以在 服务 中找到它。在 Game Center 页面上,您可能需要根据您的游戏设置排行榜和多个成就。记下您创建的排行榜和成就的 ID。

Android

#

要在 Android 上启用 Play 游戏服务

  1. 如果您尚未注册,请访问 Google Play Console 并在此处注册您的游戏。

    Google Play Console 中的“创建应用”按钮屏幕截图

  2. 仍在 Google Play Console 中,从导航菜单中选择 Play 游戏服务设置和管理配置,然后按照说明操作。

    • 这需要大量的时间和耐心。 除其他事项外,您需要在 Google Cloud Console 中设置 OAuth 同意屏幕。 如果您在任何时候感到迷茫,请参考官方的 Play 游戏服务指南

      Google Play Console 中显示游戏服务部分的屏幕截图

  3. 完成后,您可以在 Play 游戏服务设置和管理 中开始添加排行榜和成就。创建与 iOS 端相同的集合。记下 ID。

  4. 转到 Play 游戏服务 → 设置和管理 → 发布

  5. 点击 发布。别担心,这实际上并没有发布您的游戏。它只发布成就和排行榜。例如,一旦排行榜以这种方式发布,就不能取消发布。

  6. 转到 Play 游戏服务 → 设置和管理 → 配置 → 凭据

  7. 找到 获取资源 按钮。 它返回一个包含 Play 游戏服务 ID 的 XML 文件。

    xml
    <!-- 这只是一个示例 -->
    <?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) {
      // ... 处理失败 ...
    }

登录发生在后台。这需要几秒钟,因此不要在 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: '您的 Android ID',
        iOSID: '您的 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: '来自 App Store 的某个 ID',
        androidLeaderboardID: '来自 Google Play 的某个 ID',
        value: 100,
      ),
    );

    您无需检查新分数是否是玩家的最高分。平台游戏服务会为您处理。

  2. 要将排行榜显示为游戏上的叠加层,请进行以下调用:

    dart
    await GamesServices.showLeaderboards(
      iOSLeaderboardID: '来自 App Store 的某个 ID',
      androidLeaderboardID: '来自 Google Play 的某个 ID',
    );
  3. 如果您想在自己的 UI 中显示排行榜分数,您可以使用 GamesServices.loadLeaderboardScores() 获取它们。

5. 下一步

#

games_services 插件还有更多功能。使用此插件,您可以:

  • 获取玩家的图标、名称或唯一 ID
  • 保存和加载游戏状态
  • 注销游戏服务

有些成就可以是增量的。例如:“您已经收集了所有 10 件 McGuffin。”

每个游戏对游戏服务都有不同的需求。

首先,您可能需要创建此控制器,以便将所有成就和排行榜逻辑放在一个地方:

dart
import 'dart:async';

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

/// 允许授予成就和排行榜分数,
/// 并显示平台的成就和排行榜 UI 叠加层。
///
/// `package:games_services` 的外观。
class GamesServicesController {
  static final Logger _log = Logger('GamesServicesController');

  final Completer<bool> _signedInCompleter = Completer();

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

  /// 在 Game Center/Play 游戏上解锁成就。
  ///
  /// 您必须通过 [iOS][android] 参数提供成就 ID。
  ///
  /// 当游戏未登录到底层游戏服务时,不执行任何操作。
  Future<void> awardAchievement(
      {required String iOS, required String android}) async {
    if (!await signedIn) {
      _log.warning('尝试在未登录时授予成就。');
      return;
    }

    try {
      await GamesServices.unlock(
        achievement: Achievement(
          androidID: android,
          iOSID: iOS,
        ),
      );
    } catch (e) {
      _log.severe('无法授予成就:$e');
    }
  }

  /// 登录底层游戏服务。
  Future<void> initialize() async {
    try {
      await GamesServices.signIn();
      // API 不明确,因此我们正在检查以确保。上面的调用返回一个字符串,而不是布尔值,并且没有关于每个非错误结果是否意味着我们安全登录的文档。
      final signedIn = await GamesServices.isSignedIn;
      _signedInCompleter.complete(signedIn);
    } catch (e) {
      _log.severe('无法登录 GamesServices:$e');
      _signedInCompleter.complete(false);
    }
  }

  /// 启动带有成就的平台 UI 叠加层。
  Future<void> showAchievements() async {
    if (!await signedIn) {
      _log.severe('尝试在未登录时显示成就。');
      return;
    }

    try {
      await GamesServices.showAchievements();
    } catch (e) {
      _log.severe('无法显示成就:$e');
    }
  }

  /// 启动带有排行榜的平台 UI 叠加层。
  Future<void> showLeaderboard() async {
    if (!await signedIn) {
      _log.severe('尝试在未登录时显示排行榜。');
      return;
    }

    try {
      await GamesServices.showLeaderboards(
        // TODO:准备好后,更改这两个排行榜 ID。
        iOSLeaderboardID: '来自 App Store 的某个 ID',
        androidLeaderboardID: '来自 Google Play 的某个 ID',
      );
    } catch (e) {
      _log.severe('无法显示排行榜:$e');
    }
  }

  /// 将 [score] 提交到排行榜。
  Future<void> submitLeaderboardScore(int score) async {
    if (!await signedIn) {
      _log.warning('尝试在未登录时提交排行榜。');
      return;
    }

    _log.info('将 $score 提交到排行榜。');

    try {
      await GamesServices.submitScore(
        score: Score(
          // TODO:准备好后,更改这些排行榜 ID。
          iOSLeaderboardID: '来自 App Store 的某个 ID',
          androidLeaderboardID: '来自 Google Play 的某个 ID',
          value: score,
        ),
      );
    } catch (e) {
      _log.severe('无法提交排行榜分数:$e');
    }
  }
}

更多信息

#

Flutter 休闲游戏工具包包含以下模板:

  • 基础游戏:基础入门游戏
  • 纸牌游戏:纸牌游戏入门模板
  • 无尽跑酷:入门游戏(使用 Flame),玩家在游戏中无尽奔跑,躲避陷阱并获得奖励