Unity3D安卓游戏第三方SDK接入

PS:持续更新...

什么是SDK?

SDK(Software Development Kit,软件开发工具包)是一个用于构建应用程序的工具集,包含开发特定软件的必要工具、库、文档和示例代码。SDK通常由软件或硬件厂商提供,帮助开发者更容易地为特定平台、操作系统、设备或服务创建应用程序。

SDK的应用场景

1.平台开发:如Android、iOS等移动操作系统的SDK,提供了开发移动应用的所有必要资源。

2.服务集成:如云服务的SDK,帮助开发者快速集成和使用特定的云服务(如AWS、Google Cloud等)。

3.硬件开发:一些硬件厂商提供SDK,以便开发者创建与其硬件设备兼容的软件。

Google新版登录SDK--Credential

依赖项:

(1)implementation 'androidx.appcompat:appcompat:1.3.1'

(2)implementation "com.google.android.gms:play-services-auth:21.2.0"

(3)implementation "androidx.credentials:credentials:1.3.0-rc01"

(4)implementation "androidx.credentials:credentials-play-services-auth:1.3.0-rc01"

(5)implementation "com.google.android.libraries.identity.googleid:googleid:1.1.0"

接入流程:官方文档

常见错误:

1Failed to transform annotation-experimental-1.4.0.aar

2androidx.credentials.exceptions.GetCredentialProviderConfigurationException: getCredentialAsync no provider dependencies found - please ensure the desired provider dependencies are added

1.可能是设备的Google Play Service版本太低导致的,可以尝试更新设备的Google Play Service;

3During begin sign in, failure response from one tap: Missing Feature{name=auth_api_credentials_begin_sign_in, version=8}

1.提高依赖项"androidx.credentials:credentials"和"androidx.credentials:credentials-play-services-auth"的版本;

4During begin sign in, failure response from one tap: 10: [28444] Developer console is not set up correctly.

1.这个可能是因为Google后台配置问题,也可能是本地客户端ID设置错误所致;

5androidx.credentials.exceptions.GetCredentialCancellationException: activity is cancelled by the user.

1.这个可能是本地客户端ID设置错误所致;

特殊要求:

(1)设备Android系统版本最低为8.0,需要设备具备Google三件套(Google服务框架、Google Play服务、Google商店);

(2)需要设备具备VPN功能,能够访问Google服务(例如能够正常打开Google商店);

常见第三方SDK(海外)

1.Google身份验证SDK;

2.Google支付SDK;

3.AppsFlyer移动归因和营销分析SDK;

……

后台配置

Google登录SDK

1.登录Google Cloud后台,创建一个项目;

2.开启API和服务(Identity Toolkit API);

3.设置OAuth权限请求页面,添加测试用户;

4.添加OAuth2.0客户端ID,包括Web应用和Android,输入包名以及本地导包的jks的SHA-1指纹(Web应用的客户端ID将用于Google登录SDK);

5.值得注意的是,如果本地导包的jks发生改变,也应在后台更新SHA-1指纹,否则无法成功调用SDK。

Google支付SDK

1.这个需要Google开发者账号,需要在Google Play控制台配置商品信息;

2.还需要上传一个应用程序签名密钥,这个需要上传一个.zip文件,生成方法则是通过pepk将开发者账号的配套密钥的pem与本地开发所用的jks联合加密,然后导出为.zip文件;

3.注意开发者账号的配套密钥不能作为本地开发密钥,Google Play控制台为了安全考虑这种方式不被允许;

示例1

示例1将演示如何为Unity3D开发的Android项目接入Google登录SDK。

开发环境:

1.Android Studio 2024.1.0.0

2.Windows 10

3.Unity3D 2020.3.48f1c1

4.Android SDK 34 (Android Studio导出环境) Android SDK 10/11(Unity构建环境)

5.Android NDK 19.0.5232133(Unity构建环境、Android Studio导出环境)

6.JDK 11.0.24(Android Studio导出环境) JDK 1.8.0(Unity构建环境)

7.Gradle 6.1.1(Unity构建环境、Android Studio导出环境)

8.Gradle Plugin 4.0.1(Android Studio导出环境)

Unity配置:

1.Minimum api level 22

2.Target api level 34

运行环境:

1.Mumu模拟器,设备(OPPO K10 PGJM10),Android系统(12);

功能描述:

1.Unity3D 搭建UI界面,可供玩家交互,包括登录、注销登录、查看用户信息、退出游戏四个功能;

2.Android Studio接入Google身份验证SDK,需要登录、注销登录、发送提示信息三个功能;

流程描述:

1.玩家进入游戏;

2.玩家点击登录按钮,触发Google登录功能,需要对登录结果和异常进行反馈;

3.玩家点击注销登录按钮,触发Google注销登录功能,需要对注销登录结果和异常进行反馈;

4.玩家点击查看用户信息按钮,显示账户的Google id、名称、邮箱、邮箱是否验证信息,需要对异常进行反馈;

5.玩家点击退出游戏按钮,关闭游戏程序;

6.对于结果和异常反馈应采用定时关闭的提示框进行显示;

方法对应表:

方法/端口

AndroidJava

Unity3DC#

Unity3DC#)回调

登录

askLogin

OnLoginClick

OnLoginSuccess

注销登录

askLogout

OnLogoutClick

OnLogoutSuccess

发送提示信息

Null

SendTip

Null

用户信息表:

信息/端口

AndroidJava

与用户的 Google 帐号相关联的电子邮件地址

id

在条目上显示的显示名

name

用户的个人资料照片 URI

photo

用户的 Google ID Toekn

token

(1)Unity3D游戏代码

Tip.cs

using UnityEngine;
using UnityEngine.UI;

// 提示框组件:用以显示提示框信息以及交互
public class Tip : MonoBehaviour
{
    [SerializeField] Text content;
    [SerializeField] Button close;

    public bool isUsed { get; private set; }

    public bool Send(string tip)
    {
        if (isUsed || string.IsNullOrEmpty(tip)) return false;
        isUsed = true;
        content.text = tip;
        gameObject.SetActive(true);
        return true;
    }

    void Start()
    {
        close.onClick.AddListener(OnClose);
    }

    void OnClose()
    {
        gameObject.SetActive(false);
        isUsed = false;
    }
}

TipController.cs

using System.Collections.Generic;
using UnityEngine;

// 提示框控制器组件:显示提示框的统一调用接口以及控制提示框
public class TipController : MonoBehaviour
{
    public Tip tip;

    static Queue<string> contents = new Queue<string>();
    const int MAX_COUNT = 10;

    public static void Send(string content)
    {
        if (string.IsNullOrEmpty(content) || contents.Count >= MAX_COUNT) return;
        contents.Enqueue(content);
    }

    void Update()
    {
        if (contents.Count > 0 && tip.Send(contents.Peek()))
            contents.Dequeue();
    }
}

UILogic.cs

using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

// UI逻辑:UI交互相关的逻辑以及 Android 和 Unity 相互调用
public class UILogic : MonoBehaviour
{
    public Button login;
    public Button show;
    public Button logout;
    public Button quit;
    public ScrollRect showView;
    public RawImage portrait;
    public Text showText;
    public Button showViewCloseButton;

    AndroidJavaObject jo;
    UserData userData;
    bool isLoginSuccess;

    void Start()
    {
        jo = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
        login.onClick.AddListener(OnLoginClick);
        show.onClick.AddListener(OnShowClick);
        logout.onClick.AddListener(OnLogoutClick);
        quit.onClick.AddListener(OnQuitClick);
        showViewCloseButton.onClick.AddListener(OnShowViewCloseClick);
    }

    void OnDestroy()
    {
        jo.Dispose();
    }

    void OnLoginClick()
    {
        jo.Call("askLogin");
    }

    void OnShowClick()
    {
        if (!isLoginSuccess)
        {
            TipController.Send("请先进行登录。");
            return;
        }

        if (portrait.texture == null) StartCoroutine(LoadPortrait(userData.photo));

        if (string.IsNullOrEmpty(showText.text))
        {
            StringBuilder builder = new StringBuilder()
            .Append("Gmail:" + userData.id)
            .Append("\nUserName:" + userData.name)
            .Append("\nToken:" + userData.token);
            showText.text = builder.ToString();
        }

        showView.gameObject.SetActive(true);
    }

    void OnLogoutClick()
    {
        jo.Call("askLogout");
    }

    void OnQuitClick()
    {
        Application.Quit();
    }

    void OnShowViewCloseClick()
    {
        showView.gameObject.SetActive(false);
    }

    IEnumerator LoadPortrait(string uri)
    {
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(uri))
        {
            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
                TipController.Send("头像加载失败!");
            else
            {
                Texture2D texture = DownloadHandlerTexture.GetContent(www);
                portrait.texture = texture;
            }
        }
    }

    // ************************** Java Call CSharp ******************************

    // 登录成功回调:带有用户信息
    public void OnLoginSuccess(string jsonStr)
    {
        try
        {
            Debug.Log(jsonStr);
            userData = JsonUtility.FromJson<UserData>(jsonStr);
            Debug.Log(userData);
            isLoginSuccess = true;
        }
        catch (Exception ex)
        {
            TipController.Send("用户信息获取失败,请尝试重新登录。");
            Debug.LogError(ex.Message);
        }
    }

    // 注销登录成功回调
    public void OnLogoutSuccess(string value)
    {
        isLoginSuccess = false;
    }

    // 信息提示
    public void SendTip(string content)
    {
        TipController.Send(content);
    }
}

UserData.cs

// 用户信息:记录Google登录用户的信息结构体

[System.Serializable]
public struct UserData
{
    // 与用户的 Google 帐号相关联的电子邮件地址
    public string id;

    // 在条目上显示的显示名
    public string name;

    // 用户的个人资料照片 URI
    public string photo;

    // 用户的 Google ID Toekn
    public string token;

    public override string ToString()
    {
        return $"[id:{id},name:{name},photo:{photo},token:{token}]";
    }
}

(2)Android端Java代码(部分)

UnityPlayerActivity.java

// ****************************** Unity Call *******************************

public void askLogin(){
    GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
            .setFilterByAuthorizedAccounts(false)
            .setAutoSelectEnabled(true)
            .setServerClientId(getString(R.string.WEB_CLIENT_ID))
            .build();

    GetCredentialRequest request = new GetCredentialRequest.Builder()
            .addCredentialOption(googleIdOption)
            .build();
    
    CancellationSignal signal = new CancellationSignal();
    signal.setOnCancelListener(() -> {
        Log.d(TAG, "askLogin: Preparing credentials with Google was cancelled.");
        sendTip("你已取消登录!");
    });

    getCredentialManager().getCredentialAsync(
            this,
            request,
            signal,
            Executors.newSingleThreadExecutor(),
            new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
                @Override
                public void onResult(GetCredentialResponse result) {
                    handleSignIn(result);
                }

                @Override
                public void onError(GetCredentialException e) {
                    Log.e(TAG, "askLogin: ", e);
                    sendTip("登录失败!");
                }
            });
}

public void askLogout(){
    ClearCredentialStateRequest clearCredentialStateRequest = new ClearCredentialStateRequest();

    android.os.CancellationSignal cancellationSignal = new android.os.CancellationSignal();
    cancellationSignal.setOnCancelListener(() -> {
        Log.d(TAG, "askLoginOut:Preparing credentials with Google was cancelled.");
        sendTip("你已取消注销登录操作!");
    });

    if (credentialManager != null) {
        getCredentialManager().clearCredentialStateAsync(
                clearCredentialStateRequest,
                cancellationSignal,
                Executors.newSingleThreadExecutor(),
                new CredentialManagerCallback<Void, ClearCredentialException>() {
                    @Override
                    public void onResult(Void unused) {
                        Log.d(TAG, "askLoginOut:google注销登录成功");
                        sendTip("注销登录成功!");
                        UnityPlayer.UnitySendMessage("Canvas","OnLogoutSuccess","");
                    }

                    @Override
                    public void onError(ClearCredentialException e) {
                        Log.e(TAG, "askLoginOut:" , e);
                        sendTip("注销登录失败!");
                    }
                }
        );
    }
}

// ****************************** Java Call *******************************

private void sendTip(String content){
    UnityPlayer.UnitySendMessage("Canvas","SendTip",content);
}

// ****************************** Google Login SDK *******************************

private CredentialManager getCredentialManager(){
    if(credentialManager == null)
        credentialManager = CredentialManager.create(this);
    return credentialManager;
}

private void handleSignIn(GetCredentialResponse result) {
    // Handle the successfully returned credential.
    Credential credential = result.getCredential();

    if (credential instanceof PublicKeyCredential) {
        String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
        // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
    } else if (credential instanceof PasswordCredential) {
        String username = ((PasswordCredential) credential).getId();
        String password = ((PasswordCredential) credential).getPassword();
        // Use id and password to send to your server to validate and authenticate
    } else if (credential instanceof CustomCredential) {
        if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
            // Use googleIdTokenCredential and extract id to validate and
            // authenticate on your server
            GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.getData());
            String idToken = googleIdTokenCredential.getIdToken();
            try {
                JSONObject googleLoginInfoReturn = new JSONObject();
                googleLoginInfoReturn.put("id", googleIdTokenCredential.getId());
                googleLoginInfoReturn.put("name",googleIdTokenCredential.getDisplayName());
                googleLoginInfoReturn.put("photo",googleIdTokenCredential.getProfilePictureUri());
                googleLoginInfoReturn.put("token",idToken);
                Log.d(TAG, "handleSignIn: "+ googleLoginInfoReturn);
                sendTip("登录成功!");
                UnityPlayer.UnitySendMessage("Canvas","OnLoginSuccess",googleLoginInfoReturn.toString());
            } catch (JSONException e) {
                Log.e(TAG, "handleSignIn: ", e);
                sendTip("用户信息解析异常!");
            }
        } else {
            // Catch any unrecognized custom credential type here.
            Log.d(TAG, "handleSignIn: Unexpected type of credential");
            sendTip("未知的登录方式!");
        }
    } else {
        // Catch any unrecognized credential type here.
        Log.d(TAG, "handleSignIn: Unexpected type of credential");
        sendTip("未知的登录方式!");
    }
}

 视频

Google登录SDK

如果这篇文章对你有帮助,请给作者点个赞吧!  

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值