Unity中启动Android Activity并传参
源起
项目中原来的谷歌登录是Unity插件的形式添加的,现在要改成在Android中调用,要求不能继承UnityPlayerActivity,于是乎就开始查资料,花了四天才搞好,着实惭愧,好了,废话不多说
在AndroidStudio中创建工程
这里创建两个Activity,之所以创建两个,是因为发现好像从Unity的调用的的Activity中的方法,并没有创建这个Activity,所以Activity的onCreate等方法不会被调用,自然它激活别的Activity的时候的回消息的也不会被调用,所以要创建一个Activity盖在UnityActivity上面,并在创建的Activity中激活谷歌登录的Activity,小弟对Android开发不熟,如有错误还请见谅,ok继续
创建好后在app级gradle中加入引用,我使用的时候最新接口是18
implementation 'com.google.android.gms:play-services-auth:18.0.0'
修改导出形式
你也可以自定义导出的包的名字,需要在android{}中加入如下代码
android.libraryVariants.all { variant ->
variant.outputs.all {
def fileName = "自定义名字.aar"
outputFileName = fileName
}
首先添加一个工具类,是通过反射获得UnityPlayer的工具,这样就可以不用再工程中引入class.jar,避免导出aar后还要去删掉的麻烦,如果你不需要,可以跳开这步操作
package com.coreplaying.googlesignsdk;
import android.util.Log;
import android.app.Activity;
import java.lang.reflect.Method;
//获取Unity currentActivity
//获取UnityPlayer静态对象以发送信息
public class UnityTools {
private static UnityTools m_instance;
String TAG = "UnityTools";
private UnityTools(){
Init();
}
public static final UnityTools getInstance(){
if(m_instance == null){
m_instance = new UnityTools();
}
return m_instance;
}
private Class<?> unityClassType;
private Activity unityActivity;
private Method sendMethod;
private void Init(){
try{
unityClassType = Class.forName("com.unity3d.player.UnityPlayer");
unityActivity = (Activity) unityClassType.getDeclaredField("currentActivity").get(unityClassType);
}catch (Exception e){
Log.e(TAG, "Init: ", e);
}
}
public void SendUnityMessage(String objectName,String functionName,String param) {
if (unityClassType == null) {
try {
unityClassType = Class.forName("com.unity3d.player.UnityPlayer");
} catch (Exception e) {
Log.e(TAG, "SendUnityMessage: ", e);
}
}
if (unityClassType == null) {
Log.e(TAG, "SendUnityMessage: unityClassType == null");
return;
}
if (sendMethod != null) {
try {
sendMethod.invoke(unityClassType, objectName, functionName, param);
return;
} catch (Exception e) {
Log.e(TAG, "SendUnityMessage: ", e);
}
}
try {
sendMethod = unityClassType.getMethod("UnitySendMessage", String.class, String.class, String.class);
sendMethod.invoke(unityClassType, objectName, functionName, param);
} catch (Exception e) {
Log.e(TAG, "SendUnityMessage: ", e);
}
}
public Activity GetUnityActivity(){
if (unityActivity == null){
try{
unityClassType = Class.forName("com.unity3d.player.UnityPlayer");
unityActivity = (Activity) unityClassType.getDeclaredField("currentActivity").get(unityClassType);
}catch (Exception e){
Log.e(TAG, "GetUnityActivity: ",e);
return null;
}
}
return unityActivity;
}
//内存释放时的操作
@Override
protected void finalize() throws Throwable {
super.finalize();
unityClassType = null;
unityActivity = null;
}
public void Clear(){
m_instance = null;
}
}
ok我们这里写被C#调用的代码,首先是是登录
public void GoogleSign(String clientId){
this.clientId = clientId;
Activity unityActivity = UnityTools.getInstance().GetUnityActivity();
if(unityActivity == null)
{
Log.e(TAG, "GoogleSign: 未能获得currentActivity");
return;
}
Intent intent = new Intent(unityActivity,ShieldActivity.class);
intent.putExtra("clientId",clientId);
unityActivity.startActivity(intent);
}
这里激活一个Activity,并向Activity传参,参数是你的游戏的clientId,不了解clientId的同学可以参考
谷歌文档,这里我就不多说了,如果项目原来有集成谷歌登录的话,应该是有现成的
我们的新界面当然不能是不透明的,需要新加一个样式,让Activity变半透明
在你的此文件中加入如下代码
<style name="MyDialogStyleBottom" parent="Theme.AppCompat.Light.NoActionBar" >
<item name="android:windowFrame">@null</item><!--边框-->
<item name="android:windowIsTranslucent">true</item><!--半透明-->
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item><!--无标题-->
<item name="android:windowBackground">@android:color/transparent</item><!--背景透明-->
<item name="android:windowFullscreen">true</item>
<item name="android:backgroundDimEnabled">true</item><!--模糊-->
</style>
然后在你的AndroidManifest改成如下
<application>
<activity android:name=".GoogleSignActivity"/>
<activity android:name=".ShieldActivity" android:theme="@style/MyDialogStyleBottom"/>
</application>
对了,别忘记在你的Unity工程中的AndroidManifest添加标签
<activity android:name="com.example.googlesign.GoogleSignActivity" android:exported="true" />
<activity android:name="com.example.googlesign.ShieldActivity" android:exported="true" />
在你的启动的Activity(ShieldActivity)的onCreate方法中添加
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏
setContentView(R.layout.activity_main);
clientID = getIntent().getStringExtra("clientId");
此时你已经成功在Unity的上层覆盖了一个半透明的Activity,这个Activity就和普通的Activity一样可以调用
startActivityForResult
和
onActivityResult
我们将使用这两个函数来调起谷歌登录的界面和接受谷歌登录界面返回的角色信息
ok 我们继续使用谷歌登录的函数
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(clientID)
.requestEmail()
.build();
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if(account != null){
Log.d(TAG, "IsSign: 已登录,直接获取数据");
GetAccountInfo(account);
finish();
//已登录,直接获取数据
}else{
//未登录,请求登录
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, selfRequestCode);//调起谷歌登录界面
}
//接收谷歌登录界面的返回结果
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
handleSignInResult(task);
}
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
Log.d(TAG, "handleSignInResult: 登录成功");
GetAccountInfo(account);
//登录成功
} catch (ApiException e) {
Log.d(TAG, "handleSignInResult: 登录异常");
GetAccountInfo(null);
//登录异常
}
finish();
}
将你需要的数据转换成json字符串传回Unity
private void GetAccountInfo(GoogleSignInAccount account){
String userId = null;
String sessionId = null;
boolean success = false;
if(account != null){
userId = account.getId();
sessionId = account.getIdToken();
success = true;
}
UnitySendMessage(CreationJsonData(userId,sessionId,success).toString());
}
private JSONObject CreationJsonData(String userId, String sessionId, boolean success ){
JSONObject json = new JSONObject();
try {
json.put("user_id",userId);
json.put("session_id",sessionId);
json.put("success",success);
}catch (Exception e){
Log.e(TAG, "CreationJsonData: 创建json文件失败",e);
}
return json;
}
private void UnitySendMessage(String param){
Log.e(TAG, "UnitySendMessage: UnitySendMessage");
UnityTools.getInstance().SendUnityMessage("gameObjectName","functionName",param);
}
这里我们是横屏的游戏,所以加上了这一段
@Override
protected void onResume() {
if(getRequestedOrientation()!= ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
super.onResume();
}
到这里,我们的登录就基本完成了,但是这里面有个坑,你如果仅仅是这样,你在Unity中打开这个Activity是会闪退的,会报错没有布局库的引用以及google服务的引用,因为你的
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.gms:play-services-auth:18.0.0'
这两句代码仅仅是在你编译aar的工程中引用了,Unity工程中是没有引用的,所以你需要加到Unity中去
为Unity添加包
第一种是PlayerSetting中勾选如下选项
这时会在你的Assets\Plugins\Android目录下创建一个gradle的模板,打开这个模板,在dependencies中添加那两句代码,效果如下
dependencies {
implementation project(':unityLibrary')
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.gms:play-services-auth:18.0.0'
}
还有一种更强大的办法,这里推荐一个为Unity下载包的插件,将
将上面的包复制到你的Aesset下的第三方插件目录,然后在你放SDKUnity侧的C#代码的目录下新建一个Editor文件夹,准备一个xml文件,文件内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<dependencies>
<!-- See https://github.com/googlesamples/unity-jar-resolver#usage for
how to configure dependencies -->
<androidPackages>
<androidPackage spec="com.google.android.gms:play-services-auth:18.0.0" />
<androidPackage spec="com.android.support.constraint:constraint-layout:1.1.3" />
</androidPackages>
<!-- iOS Cocoapod dependencies can be specified by each iosPod element. -->
<iosPods>
<iosPod name="GoogleSignIn" version=">= 4.0.2" bitcodeEnabled="false"
</iosPod>
</iosPods>
</dependencies>
含义我就不多做解释了
然后如下操作
这样就会把对应的aar文件下载到你的Assets\Plugins\Android目录下,这时你的Activity就可以正常打开并调用起谷歌登录的界面了
退出的话代码如下
public void GoogleSignOut(){
Activity unityActivity = UnityTools.getInstance().GetUnityActivity();
if(unityActivity == null){
Log.e(TAG, "GoogleSignOut: unityActivity == null");
return;
}
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(unityActivity,gso);
mGoogleSignInClient.signOut()
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
UnityTools.getInstance().SendUnityMessage("gameObjectName","functionName","");
}
});
}
Unity侧代码
好了,此时你的AndroidStudio端的代码就完成了,Unity端的调用代码就很简单了,只是简单的交互,嗲用Android的方法和接收Android的数据即可
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityObj = jc.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject googleSignObj = new AndroidJavaObject("com.example.googlesign.GoogleSignActivity");
googleSignObj.Call("GoogleSign", clienId);
googleSignObj.Call("GoogleSignOut");
接收消息的方法我就不啰嗦了网上有很多,要是实在不会的可以参考这里写的很详细
感谢一下下面的博客给了我很大的帮助
https://blog.csdn.net/u013341672/article/details/80266116?utm_source=blogxgwz0