Unity3D安卓游戏第三方SDK接入

news2024/9/20 7:50:06

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2092692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL高阶练习题1- 寻找面试候选人

目录 题目 准备数据 分析数据 实现代码 总结 题目 返回 所有面试候选人 的姓名 name 和邮件 mail 。当用户满足以下两个要求中的 任意一条 &#xff0c;其成为 面试候选人 : 该用户在 连续三场及更多 比赛中赢得 任意 奖牌。该用户在 三场及更多不同的 比赛中赢得 金牌&…

Linux——用户和权限

root用户&#xff08;超级管理员&#xff09; root用户拥有最大的系统操作权限&#xff0c;而普通用户在许多地方的权限是受限的。 ****************** 使用普通用户在根目录下创建文件夹 切换到root用户后&#xff0c;继续尝试 普通用户的权限&#xff0c;一般在其HOME目录…

【linxu】虚拟环境中Python 版本错乱:深入探究 Linux 虚拟环境的识别问题

【linxu】虚拟环境中Python 版本错乱&#xff1a;深入探究 Linux 虚拟环境的识别问题 问题描述&#xff1a;在服务器上&#xff0c;我配置了一个虚拟环境&#xff0c;明确指定使用 Python 3.8 版本。然而&#xff0c;当我激活该环境并检查 Python 版本时&#xff0c;意外地发现…

IO练习--随机点名

随机点名器1 需求: 有一个文件里面存储了班级同学的信息&#xff0c;每一个信息占一行。 格式为:张三-男-23 要求通过程序实现随机点名器。 运行效果: 第一次运行程序:随机同学姓名1(只显示名字) 第二次运行程序:随机同学姓名2(只显示名字) 第三次运行程序:随机同学姓名3(只显…

数学建模强化宝典(2)linprog

一、介绍 linprog 是 MATLAB 中用于解决线性规划问题的函数。线性规划是一种优化方法&#xff0c;它尝试在满足一组线性等式或不等式约束的条件下&#xff0c;找到一个线性目标函数的最大值或最小值。linprog 函数适用于求解形如以下问题的线性规划问题&#xff1a; minimizecT…

Native开发与逆向第六篇 -字符串加密与hook

开发demo 写一个简单的字符串加密处理&#xff0c;将字符串字符转成ASCII十六进制值 std::string StrToHex(std::string str){unsigned char c;char buf[3];std::string result "";std::stringstream ss;ss << str;while (ss.read((char *)(&c), sizeof…

Python数据类型转换背后的逻辑

文末赠免费精品编程资料~~ 今天让我们深入浅出地探索Python数据类型转换的奥秘。Python&#xff0c;作为一门灵活的编程语言&#xff0c;其强大的数据类型转换能力是日常编码中不可或缺的一部分。今天&#xff0c;我们将一步步揭开类型转换背后的逻辑&#xff0c;让你从新手进…

【wsl2】从C盘迁移到G盘

参考大神 C盘的ubuntu22.04 非常大&#xff0c;高达30g 迁移后就只有几百M了&#xff1a; 右键有一个move没有敢尝试 迁移过程 Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved.Install the latest PowerShell for new features and improveme…

HCCL集合通信算法开发Hello World示例(超详细)

本文给读者介绍了HCCL算法开发所涉及的概念和流程&#xff0c;并通过一个样例将前文介绍的内容串联起来。本文定位为HCCL算法开发的入门介绍&#xff0c;读者读完后&#xff0c;可结合HCCL开放代码仓中的算法样例&#xff0c;做深入研究。 1 什么是集合通信 集合通信定义了一…

Http的get请求中的URL中的占位符参数和查询参数有什么区别

Http的GET请求中的URL中的占位符参数和查询参数在功能、位置和用途上存在明显的区别。 占位符参数&#xff08;Path Variables&#xff09; 定义与位置&#xff1a;占位符参数是通过URL模板中的{}定义的&#xff0c;它们位于URL的路径&#xff08;path&#xff09;部分。例如…

C#文件的输入和输出

一个文件是一个存储在磁盘中带有指定名称和目录路径的数据集合.当打开文件进行读写时,它变成一个流.从根本上说,流是通过通信路径传递的字节序列.有两个主要的流:输入流和输出流.输入流用于从文件读取数据,输出流用于向文件写入数据. C#I/O类 System.IO命名空间有各种不同的类…

带权重的随机算法

假设有10名学生&#xff0c;其中5个男生&#xff0c;5个女生。 要求点到男生的概率为70%&#xff0c;女生的概率为30%。 给男生和女生设置权重&#xff0c;其中男生权重为7&#xff0c;女生权重为3。 public class Test02_case2 {public static void main(String[] args) th…

Expected expression after operator

这个错误直译过来就是:运算符号后没有预期的表达式 这个错误通常出现在编程语言中&#xff0c;尤其是在编写C或C等类型语言的时候&#xff0c;它意味着在源代码中遇到了一个操作符&#xff08;比如 , -, *, /, , 等等&#xff09;&#xff0c;但在该操作符后面没有紧跟相应的表…

【最新华为OD机试E卷】最大利润-贪心的商人(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

Dolphinscheduler 3.2.0版本参数传递并使用switch任务进行判断

原文阅读&#xff1a;【巨人肩膀社区博客分享】3.2.0版本参数传递并使用switch任务进行判断 目标&#xff1a;根据日期判断执行哪项子任务 &#xfeff; 调度器版本&#xff1a;3.2.0 在这个版本中官方支持的参数传递任务类型有6中&#xff0c;分别为shell,sql,procedure,py…

网络压缩之网络剪枝(network pruning)

网络剪枝&#xff08;network pruning&#xff09;就是要把网络里面的一些参数剪掉。剪枝就是修剪的意思&#xff0c;把网络里面的一些参数剪掉。为什么可以把网络里面的一些参数剪 掉呢&#xff1f;这么大的网络里面有很多很多的参数&#xff0c;每一个参数不一定都有在做事。…

AcWing 897. 最长公共子序列

动态规划就是多见识应用题就完事儿了&#xff0c;也没有什么好说的。 讲解参考&#xff1a; 【E05 线性DP 最长公共子序列】 #include<iostream> #include<algorithm> #define N 1010 using namespace std; char a[N],b[N]; int n,m; int f[N][N]; int main(){…

欧拉 函数

互质&#xff1a; 互质是公约数只有1的两个整数&#xff0c;叫做互质整数。公约数只有1的两个自然数&#xff0c;叫做互质自然数&#xff0c;后者是前者特殊情况。 &#xff08;1和-1与所有整数互质&#xff0c;而且它们是唯一与0互质的整数&#xff09; 互质的判断方法&…

微信公众号文章导出工具 100%还原原文样式:wechat-article-exporter

wechat-article-exporter是一款微信公众号文章导出工具&#xff0c;能够100%还原原文样式&#xff0c;工具受 WeChat_Article 项目的启发所写&#xff0c;目前支持 搜索公众号和公众号内文章&#xff0c;导出文章为包含图片和样式文件的HTML格式&#xff08; (打包了图片和样式…

中仕公考:这样备考,你天生就是公务员!

根据上岸学员的反馈&#xff0c;小编发现了一些共通点&#xff0c;无论是在职备考还是全职备考&#xff0c;只要做到以下几点&#xff0c;不上岸那是不可能的! 1. 作息规律&#xff0c;早起不熬大夜。每天按时早起&#xff0c;挤出时间用来学习&#xff0c;晚上不熬夜学习到很…