一、前言:

大家好,我是完美句号!欢迎来到 HarmonyOS 5 开发实战系列。本系列致力于为开发者提供实用的技术方案和即拿即用的代码示例,帮助大家快速掌握 HarmonyOS Next 应用开发中的核心功能。在HarmonyOS 5鸿蒙系统中,基于HarmonyOS 5版本,开发者可以快速构建游戏应用,实现游戏基本功能,如游戏登录、游戏优化、游戏运营、游戏推广等。本章将带领开发者快速掌握游戏登录服务的使用方法,帮助开发者快速实现游戏登录功能。

Game Service Kit(游戏服务)提供快速、低成本构建游戏基本能力与游戏场景优化服务,有效提升游戏开发效率,帮助开发者进行游戏运营。开发者应用可以允许用户使用华为账号登录游戏,从而迅速推广游戏,共享华为庞大的用户价值。开发者可以快速实现实名认证、游戏资产转移、防沉迷等功能,快速低成本本地构建游戏基本能力,并基于用户和内容的本地化进行深度的游戏运营。


二、游戏登录服务介绍:

2.1 场景:

  • 1.游戏登录:用户使用华为帐号快速、便捷地登录您的游戏,免去繁琐的注册验证步骤。
  • 2.游戏优化:根据游戏场景优化游戏性能,进一步提升玩家的游戏体验。
  • 3.游戏运营:接入游戏服务后可与华为游戏中心合作开展各种游戏宣传活动,促进用户活跃、提升付费率。
  • 4.游戏推广:通过详细的游戏数据分析和华为游戏中心强大的分发能力,助您有效触达优质玩家,快速推广游戏。

2.2 优势:

  • 1.本地运营:提供基于用户的本地化运营。
  • 2.快速集成:支持开发者快速低成本地构建游戏基本功能。
  • 3.优化体验:基于华为帐号登录,用户一触即达,无需复杂操作。

2.3 AGC控制台创建应用:

步骤一:登录AppGallery Connect(简称AGC),点击“我的项目”,在项目中点击“添加项目”,输入项目名称后,点击“创建并继续”。

img

步骤二:项目创建完成后,点击项目设置页面“添加应用”按钮,在“添加应用”页面中设置参数后,点击“确认”。注:应用包名需与DevEco Studio 创建HarmonyOS应用工程的Bundle name一致,且应用分类选择“游戏”。

img


2.4 添加公钥指纹:

当应用需要使用Account Kit(华为账号服务)、Call Kit(通话服务)、Game Service Kit(游戏服务)、Health Service Kit(运动健康服务)、IAP Kit(应用内支付服务)、Live View Kit(实况窗服务,当需要使用Push Kit时必须执行此步骤)、Map Kit(地图服务)、Payment Kit(华为支付服务)、Push Kit(推送服务)等开放能力的一种或多种时,为正常调试运行应用,需要预先添加公钥指纹。

  • 步骤一:申请应用证书(.cer)、Profile(.p7b)文件。
  • 步骤二:添加公钥指纹。
  • 步骤三:真机或模拟器需要在设置界面中登录华为实名认证的账号。

2.5 配置APP ID和Client ID:

登录AppGallery Connect平台,在“我的项目”中选择目标应用,获取“项目设置 > 常规 > 应用”的APP ID和Client ID。在工程的entry模块module.json5文件中,新增metadata并配置client_id和app_id,如下所示:

img


三、游戏登录服务开发步骤:

img

3.1 开发步骤:

步骤一:导入Account Kit模块、Game Service Kit模块及相关公共模块。

import { AbilityConstant, UIAbility, Want, common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { JSON, util } from '@kit.ArkTS';
import GameKitUtil from '../utils/GameKitUtil';

步骤二:导入相关模块后,游戏需先调用init完成初始化。初始化时Game Service Kit会弹出隐私协议弹框,玩家必须在同意隐私协议后,游戏才能调用其他接口,否则在调用其他接口时会返回1002000011错误码。调用init接口时严格要求继承UIAbility,并且获取上下文的时机是onWindowStageCreate生命周期中页面加载成功后。

onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
      // 游戏服务初始化
      GameKitUtil.init(this.context);
    });
  }

步骤三:初始化成功后,游戏调用on接口注册playerChanged事件监听。当玩家信息变化后,比如游戏号切换等,将会执行playerChanged事件回调方法,以通知开发者进行本地缓存清理和再次调用unionLogin接口等。

gamePlayer.on('playerChanged', (result: gamePlayer.PlayerChangedResult) => {
          if (result.event === gamePlayer.PlayerChangedEvent.SWITCH_GAME_ACCOUNT) {
            // 游戏号已切换,完成本地缓存清理工作后,再次调用unionLogin接口等
            AppStorage.setOrCreate("LoginStatus", 0);
          }
        })

步骤四:初始化成功后,游戏可调用unionLogin接口进行联合登录,Game Service Kit向玩家展示联合登录弹窗,通过联合登录接口获取到accountName。

// 游戏联合登录
  public unionLogin(context: common.UIAbilityContext, showLoginDialog?: boolean): void {
    hilog.info(0x0000, 'testTag', 'Enter unionLogin.');
    const thirdAccountInfo: gamePlayer.ThirdAccountInfo = {
      accountName: 'NutpiGameAccountName', // 游戏官方账号名
      accountIcon: $r('app.media.nutpi_logo')  // 游戏官方账号图标资源信息
    };
    const unionLoginParam: gamePlayer.UnionLoginParam = {
      showLoginDialog: true,  // 默认为false;当需要切换账号或进行其他操作时,该值为true
      thirdAccountInfos: [
        thirdAccountInfo  // 游戏官方账号图标大小总和最大支持35KB
      ]
    };
    try {
      gamePlayer.unionLogin(context, unionLoginParam).then((result: gamePlayer.UnionLoginResult) => {
        hilog.info(0x0000, 'testTag', 'Succeeded in union login.');
        this.gamePlayerId = result.localPlayer?.gamePlayerId ?? '';
        this.teamPlayerId = result.localPlayer?.teamPlayerId ?? '';
        const accountName = result.accountName;
        if (accountName === 'hw_account') {
          promptAction.showToast({
            message: "玩家选择使用华为账号登录方式!"
          })
          if (result.needBinding) {
            // 当needBinding为true时(关联场景),华为玩家标识需与游戏官方账号进行关联绑定
            // 应调用createAuthorizationWithHuaweiIDRequest接口创建授权请求(授权获取手机号)
            this.createAuthorizationAndVerifyPlayer(context);
          } else {
            // 当needBinding为false时(转移场景),调用createLoginWithHuaweiIDRequest接口创建登录请求
            this.createLoginAndVerifyPlayer(context);
          }
        } else if (accountName === 'official_account' || accountName === thirdAccountInfo.accountName) {
          // 1.进行游戏官方账号自行登录处理
          // 2.登录完成后,则调用verifyLocalPlayer接口进行合规校验
          // 3.合规检验成功后,在玩家创建角色时,游戏必须调用savePlayerRole将玩家角色信息上报。
          promptAction.showToast({
            message: "玩家选择使用游戏官方账号登录方式!"
          })
          // 玩家选择游戏官方帐号成功登录后,调用合规校验接口
          // thirdOpenId表示游戏官方帐号ID
          // this.thirdOpenId = '123xxxx';
          // this.verifyLocalPlayer(context);
        }
      }).catch((error: BusinessError) => {
        hilog.error(0x0000, 'testTag', 'Failed to union login. Cause: %{public}s', JSON.stringify(error) ?? '');
      })
    } catch (error) {
      const err = error as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to union login. Cause: %{public}s', JSON.stringify(err) ?? '');
    }
  }

步骤五:华为账号认证与授权。通过联合登录接口获取到accountName,若值为“hw_account”时,通过Account Kit对应的创建授权/登录请求接口,获取用于服务器校验的Authorization Code信息;若值为unionLogin接口传入的thirdAccountInfo.accountName或“official_account”时,进行游戏官方账号自行登录处理。

if (accountName === 'hw_account') {
          promptAction.showToast({
            message: "玩家选择使用华为账号登录方式!"
          })
          if (result.needBinding) {
            // 当needBinding为true时(关联场景),华为玩家标识需与游戏官方账号进行关联绑定
            // 应调用createAuthorizationWithHuaweiIDRequest接口创建授权请求(授权获取手机号)
            this.createAuthorizationAndVerifyPlayer(context);
          } else {
            // 当needBinding为false时(转移场景),调用createLoginWithHuaweiIDRequest接口创建登录请求
            this.createLoginAndVerifyPlayer(context);
          }
        } else if (accountName === 'official_account' || accountName === thirdAccountInfo.accountName) {
          // 1.进行游戏官方账号自行登录处理
          // 2.登录完成后,则调用verifyLocalPlayer接口进行合规校验
          // 3.合规检验成功后,在玩家创建角色时,游戏必须调用savePlayerRole将玩家角色信息上报。
          promptAction.showToast({
            message: "玩家选择使用游戏官方账号登录方式!"
          })
          // 玩家选择游戏官方帐号成功登录后,调用合规校验接口
          // thirdOpenId表示游戏官方帐号ID
          // this.thirdOpenId = '123xxxx';
          this.verifyLocalPlayer(context);
        }
// 合规校验
  private verifyLocalPlayer(context: common.UIAbilityContext): void {
    hilog.info(0x0000, 'testTag', `Enter verifyLocalPlayer.`);
    let request: gamePlayer.ThirdUserInfo = {
      thirdOpenId: this.thirdOpenId, // 游戏官方帐号ID
      isRealName: true, // 玩家是否实名,该值为true时表示已实名,为false时表示未实名
    };

    try {
      gamePlayer.verifyLocalPlayer(context, request).then(() => {
        hilog.info(0x0000, 'testTag', `Succeeded in verifying local player.`);
        // 合规检验成功后,允许玩家进入游戏
      }).catch((error: BusinessError) => {
        hilog.error(0x0000, 'testTag', `Failed to verify local player. Cause: %{public}s`, JSON.stringify(error) ?? '');
        // 合规检验失败,不允许玩家进入游戏
      });
    } catch (error) {
      hilog.error(0x0000, 'testTag', `Failed to verify local player. Cause: %{public}s`, JSON.stringify(error) ?? '');
      // 合规检验失败,不允许玩家进入游戏
    }
  }

步骤六:需要华为玩家标识与游戏官方账号绑定(needBinding为true)。

调用createAuthorizationWithHuaweiIDRequest创建授权请求并设置参数。若需授权获取用户手机号,应先完成“获取用户手机号”的scope权限申请,并在authRequest.scopes中传入“phone”。调用AuthenticationController对象的executeRequest方法执行授权请求,并在Callback中处理授权结果,从授权结果中解析出头像昵称。

// 需要华为玩家标识与游戏官方账号绑定
  private createAuthorizationAndVerifyPlayer(context: common.UIAbilityContext): void {
    hilog.info(0x0000, 'testTag', 'Enter createAuthorizationAndVerifyPlayer.');
    // 创建授权请求,并设置参数
    let authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
    // 获取头像昵称、手机号需要传入如下scope
    authRequest.scopes = ['profile', 'phone'];
    // 用户是否需要登录授权
    authRequest.forceAuthorization = true;
    authRequest.state = util.generateRandomUUID();
    try {
      // 执行授权请求
      let controller = new authentication.AuthenticationController(context);
      controller.executeRequest(authRequest, (err, data) => {
        if (err) {
          hilog.error(0x0000, 'testTag', 'Failed to auth. Cause: %{public}s', JSON.stringify(err) ?? '');
          return;
        }
        const response = data as authentication.AuthorizationWithHuaweiIDResponse;
        const state = response.state;
        if (state != undefined && authRequest.state != state) {
          hilog.error(0x0000, 'testTag', 'Failed to auth, state is different.');
          return;
        }
        hilog.info(0x0000, 'testTag', `Succeeded in authenticating.`);
        let authorizationWithHuaweiIDCredential = response.data!;
        let avatarUri = authorizationWithHuaweiIDCredential.avatarUri;
        let nickName = authorizationWithHuaweiIDCredential.nickName;
        let authorizationCode = authorizationWithHuaweiIDCredential.authorizationCode;
        promptAction.showToast({
          message: `登录用户信息:${nickName}`
        })
        // 开发者处理vatarUri, nickName, authorizationCode信息,并进行玩家信息校验:
        // 1.游戏客户端将玩家信息(Authorization Code、玩家标识等)上传给游戏开发者服务器
        // 2.开发者服务器通过authorizationCode调用华为账号服务器的获取凭证AccessToken接口,获取玩家的AccessToken
        // 3.开发者服务器通过AccessToken调用华为账号服务器的获取用户信息接口,获取授权手机号
        // 4.开发者服务器调用华为游戏服务器的获取玩家信息接口,根据AccessToken获取服务器侧的玩家标识
        // 5.开发者服务器将客户端与服务器分别获取的玩家标识进行一致性核验

        // this.thirdOpenId = '123xxxx'; // thirdOpenId表示游戏官方帐号ID
        // 游戏服务器校验玩家信息成功后,则进行如下操作:
        // 1.判断授权获取的手机号是否注册游戏官方账号,如未注册,则进行注册;
        // 2.判断teamPlayerId是否绑定游戏官方账号,如未绑定,则进行绑定;
        // 3.调用合规校验接口
        // 4.合规检验成功后,在玩家创建角色时,游戏必须调用savePlayerRole将玩家角色信息上报
        // if (!this.isBinding(this.teamPlayerId)) {
          // 将玩家标识与游戏官方帐号进行绑定
        //   this.bindPlayer(context, this.thirdOpenId, this.teamPlayerId);
        // }
        // 进行合规校验
        // this.verifyLocalPlayer(context);
      })
    } catch (error) {
      const err = error as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to auth. Cause: %{public}s', JSON.stringify(err) ?? '');
    }
  }

步骤七:不需要华为玩家标识与游戏官方账号绑定(needBinding为false)。调用createLoginWithHuaweiIDRequest创建登录请求并设置参数。调用AuthenticationController对象的executeRequest方法执行登录请求,并在Callback中处理登录结果,获取到AuthorizationCode。

// 不需要华为玩家标识与游戏官方账号绑定
  private createLoginAndVerifyPlayer(context: common.UIAbilityContext): void {
    hilog.info(0x0000, 'testTag', `Enter createLoginAndVerifyPlayer.`);
    // 创建登录请求,并设置参数
    let loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();
    // 当用户未登录华为帐号时,是否强制拉起华为帐号登录界面
    loginRequest.forceLogin = true;
    loginRequest.state = util.generateRandomUUID();
    try {
      // 执行授权请求
      let controller = new authentication.AuthenticationController(context);
      controller.executeRequest(loginRequest, (err, data) => {
        if (err) {
          hilog.error(0x0000, 'testTag', 'Failed to login. Cause: %{public}s', JSON.stringify(err) ?? '');
          return;
        }
        const response = data as authentication.AuthorizationWithHuaweiIDResponse;
        const state = response.state;
        if (state != undefined && loginRequest.state != state) {
          hilog.error(0x0000, 'testTag', 'Failed to login, state is different.');
          return;
        }
        hilog.info(0x0000, 'testTag', `Succeeded in logining.`);
        let loginWithHuaweiIDCredential = response.data!;
        let code = loginWithHuaweiIDCredential.authorizationCode;
        promptAction.showToast({
          message: `登录用户AuthorizationCode:${code}`
        })
        // 开发者处理authorizationCode
        // 1.游戏客户端将玩家信息(Authorization Code、玩家标识等)上传给游戏开发者服务器
        // 2.开发者服务器通过authorizationCode调用华为账号服务器的获取凭证AccessToken接口,获取玩家的AccessToken
        // 3.开发者服务器调用华为游戏服务器的获取玩家信息接口,根据AccessToken获取服务器侧的玩家标识
        // 4.开发者服务器将客户端与服务器分别获取的玩家标识进行一致性核验
        // 5.玩家选择游戏官方帐号成功登录后,无需绑定,但需要调用合规校验接口
        // 6.合规检验成功后,在玩家创建角色时,游戏必须调用savePlayerRole将玩家角色信息上报
        // this.thirdOpenId = ''; // thirdOpenId表示游戏官方帐号ID,此处赋值为空字符串
        // this.verifyLocalPlayer(context);
      })
    } catch (error) {
      const err = error as BusinessError;
      hilog.error(0x0000, 'testTag', 'Failed to login. Cause: %{public}s', JSON.stringify(err) ?? '');
    }
  }

img

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐