• Unity3D工程作为库内嵌到安卓原生开发指南


    前言

    本案例使用Unity 2020.3.39f1c1与Android Studio 2021.3.1
    前提条件
    1.你的Unity已经安装好Android平台模块,可以在UnityHub中查看。
    2.Android Studio IDE已经安装好了,测试工程项目能正常运行。(如果未安装Android Studio 可以查看我的另外一篇文章《Android Studio IDE安装指南》)

    一、创建Unity项目并导出

    首先创建一个Unity空项目,在空场景中创建一个空物体名字为UnityGameDataMgr

    注意:这个名字是等会再Android中向Unity发送消息时的API中必须要传的参数

    然后在UnityGameDataMgr空物体上添加一个脚本,脚本中的代码如下:

    using UnityEngine;
    using System;
    using UnityEngine.UI;
    
    public class GameDataMgr : MonoBehaviour
    {
        public Button Btn_UnityExit;
        public Button Btn_ShowMainActivityQuit;
        public Button Btn_ShowMainActivityUnload;
        public Text Txt_FormAndroidMessage;
        string userData = "用户信息(UseData):张三,22,男";
    
        private void Awake()
        {
            DontDestroyOnLoad(this);
        }
        
        private void Start()
        {
            Btn_UnityExit.onClick.AddListener(OnQuitUnity);
            // 点击ShowMainActivityUnload按钮调用
            Btn_ShowMainActivityUnload.onClick.AddListener(() =>
            {
                CallAndroidMethod("showMainActivity", false, ("UnloadSend:" + userData));
            });
            // 点击ShowMainActivityQuit按钮调用
            Btn_ShowMainActivityQuit.onClick.AddListener(() =>
            {
                CallAndroidMethod("showMainActivity", true, ("QuitSend:" + userData));
            });
        }
    
        /// 
        /// 调用UnityGameActivity的方法
        /// 
        /// Android方法名称
        /// 方法参数1:是否结束UnityGameActivity
        /// 方法参数2:具体数据
        private void CallAndroidMethod(string methodName, bool isFinish, string data)
        {
    #if UNITY_ANDROID
            try
            {
                AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
                jo.Call(methodName, isFinish, data);
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
            }
    #endif
        }
    
        /// 
        /// Android调用方法 在安卓UnityGameActivity OnCreate调用
        /// 
        /// 
        public void sendMessageToUnity(string str)
        {
            Debug.Log("Unity获取Android MainActivity发送过来的Token信息: " + str);
            Txt_FormAndroidMessage.text = "来自Android MainActivity信息:" + str;
        }
    
        #region Unity程序原生方法
        void OnQuitUnity()
        {
            Application.Quit();
        }
        #endregion
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    Unity UGUI Canvas布局层级结构如下:
    在这里插入图片描述
    在这里插入图片描述

    然后在File->Build Settings中将平台切换到Android
    在这里插入图片描述

    按照上图的指示操作然后在Player Settings中配置Other Settings其中的选项
    1.设置Package Name。这个必须要与Android中的包名一致(不一致好像也可以)。
    2.设置Minimum API Level 。这个一定要与Android Studio中保持一致,如果不一致,在Android Studio中发布时会报错。
    3.设置Scripting BackedIL2CPP,设置Api Compatibility Level 为**.NET Standard 2.0**。
    4.4.设置Target Architectures。选择 ARMv7ARM64(可选)
    在这里插入图片描述
    4.设置签名(非必要)。我这里没有设置,但是之前设置了也能正常运行,这里跟签名应该没有太大关系,我的都没有设置,看下图(如果使用IL2CPP打包APK出错可能就需要设置)
    在这里插入图片描述
    5.最后就是点击Build Settings中的Export导出Android中需要的项目,导出的文件夹与Unity项目的关系,如果你是第一次看着我的博客教程跟着做,那么请按照我的文件夹的命名与结构来做,因为在Android Studio中需要根据路径引入Unity发布出来的安卓项目。

    Unity项目目录与安卓项目目录之间的关系
    在这里插入图片描述

    至此,Unity中设置已经完成。

    二、创建Android项目并引入

    创建安卓项目,选择Empty Activity->Next
    请添加图片描述
    请添加图片描述

    注意:Minimum SDK版本与Unity两者保持一致

    注意:创建出来的Android项目下方没有任何报错才可以,像下图一样
    在这里插入图片描述
    创建完成后,开始导入Unity Build出来的包

    三、导入Unity项目(模块)

    这里有两种做法,

    第一种,直接打开刚刚用Unity导出的项目文件夹做为一个Android项目;
    第二种,把导出的项目里面的unityLibrary文件夹做为一个模块来用;

    这里选择第二种方式导入
    1.导入unityLibrary模块
    File -> New -> Import Module
    在这里插入图片描述
    在这里插入图片描述
    不出意外这个时候应该会报错
    在这里插入图片描述

    导入之后会出现一系列的错误提示,不过不要慌,问题不大,下面开始进行配置。

    2.unityLibrary模块配置
    2.1.项目settings.gradle文件配置
    在项目settings.gradle文件里添加两行代码
    在这里插入图片描述

    include ':app',':unityLibrary'
    rootProject.name = "AndroidAPP"
    //--上面两句应该本身就有不需要添加--
    
    //括号里的是你的unityLibrary所在的路径
    project(':unityLibrary').projectDir=new File('..\\UnityAndroidBuild\\unityLibrary')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    在项目settings.gradle文件里添加

    flatDir {
    	dirs "${project(':unityLibrary').projectDir}/libs"
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    项目settings.gradle文件最终配置就是上面这样的。

    2.2.app模块文件配置
    在app模块下的build.gradle文件里面加上

    implementation project(':unityLibrary')
    implementation fileTree(dir: project(':unityLibrary').getProjectDir().toString() + ('\\libs'), include: ['*.jar'])
    
    • 1
    • 2

    在这里插入图片描述
    同时还是在app模块下的build.gradle文件里面添加引入SO库架构,如下代码

    注意:添加引入SO库架构,如果不添加,构建出来的app运行时会闪退,并且报错:can not find ‘libmain.so’

    ndk {
          // 设置支持的SO库架构,第三方给的so库哪几种架构,就配置这几种架构
          abiFilters 'armeabi', 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    app模块下的strings.xml文件里加上

    <string name="game_view_content_description">Game view</string>
    
    • 1

    在这里插入图片描述
    点击蓝色字体 Sync Now按钮,结果
    在这里插入图片描述
    如果出现上面这个错误在上图序号①所指 项目gradle.properties文件加入一下代码(没有报错的不用加)

    unityStreamingAssets=.unity3d, google-services-desktop.json, google-services.json, GoogleService-Info.plist
    
    • 1

    在这里插入图片描述
    继续点击 Sync Now 按钮,编译成功,无报错!
    在这里插入图片描述

    4.简单交互Demo创建与运行
    首先更改app模块MainActivity.java类
    在这里插入图片描述

    MainActivity类完整代码如下:

    package com.test.androidapp;//自己的APP包名
    import androidx.appcompat.app.AppCompatActivity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity  {
    
        private Button btn;
        private TextView textView;
        private String tokenStr="这是一个tokenStr数据";
        private String unityDataStr="";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
            btn = findViewById(R.id.button);
            textView=findViewById(R.id.textView);
            //注册监听器
            btn.setOnClickListener(this::onClickStartGameBtn);
            handleIntent(getIntent());
        }
    
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
    
            handleIntent(intent);
            setIntent(intent);
        }
    
        int i=0;
        //按键点击进入UNITY
        private void onClickStartGameBtn(View view) {
            System.out.println("触发点击进入游戏的按钮事件");
            Intent intent=new Intent(this, UnityGameActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
            i=i+1;//模拟点击进入Unity次数 每次点击+1
            intent.putExtra("TokenCode", tokenStr+"{"+i+"}");
            //startActivity(intent);
            startActivityForResult(intent, 1);//requestCode >=0即可
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == 1) {
                Bundle extras = data.getExtras();
                if (extras!=null){
                    unityDataStr=extras.getString("UnityData");
                }
                textView.setText(unityDataStr);
                System.out.println(unityDataStr);
            }
        }
    
        //当UnityActivityGame结束后返回
        //这个是处理来自UnityGameActivity传过来的信息
       private void handleIntent(Intent intent) {
           if(intent == null || intent.getExtras() == null)  return ;
    
            if(intent.getExtras().containsKey("UnityData")) {
    
                unityDataStr = intent.getStringExtra("UnityData");
                textView.setText(unityDataStr);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    在activity_main.xml里面新建一个Button按钮和TextView组件
    在这里插入图片描述
    MainActivity类中的R.id.buttonactivity_main.xml中的Button控件id属性的值,R.id.textView同理。

    app模块新建UnityGameActivity类作为进入Unity的入口
    在这里插入图片描述
    UnityGameActivity类完整代码如下:

    package com.test.androidapp;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import com.unity3d.player.UnityPlayer;
    import com.unity3d.player.UnityPlayerActivity;
    
    //进入到这个类就进入了unity游戏画面
    public class UnityGameActivity extends UnityPlayerActivity {
    
        private String tokenStr;
        private static final String TAG = "UnityGameActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            GetTokenFromMainActivity();
        }
    
        //进入UnityGameActivity就调用sendMessageToUnity给Unity Text组件赋值
        private void  GetTokenFromMainActivity(){
            Bundle bundle=getIntent().getExtras();
            if (bundle!=null)
                tokenStr= bundle.getString("TokenCode");
            sendMessageToUnity(tokenStr);
        }
    
        //发送给Unity的数据 Android-》Unity
        private void sendMessageToUnity(String str) {
    
            Log.i(TAG, "Android传给unity的信息: " + str);
            //其中GameDataMgr是unity生成的gameobject,脚本要挂在上面。AndroidToUnity是unity里实现的方法。str是传过去的值。
            UnityPlayer.UnitySendMessage("UnityGameDataMgr", "sendMessageToUnity", str);
        }
    
        //返回MainActivity并将unityData数据传递给MainActivity显示( Unity-》Android)
        //unity 退出应用之前将Unity的数据传递给MainActivity
        //isFinish为false 直接返回MainActivity;当为true,结束此活动返回MainActivity
        public void showMainActivity(boolean isFinish,String unityDataStr) {
    
            if (!isFinish){
                Intent intent=new Intent(this,MainActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                intent.putExtra("UnityData",unityDataStr);
                startActivity(intent);
            }
            else{
                Bundle bundle=new Bundle();
                bundle.putString("UnityData",unityDataStr);
                Intent intent=new Intent();
                intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                setResult(1,intent);
                intent.putExtras(bundle);
                finish();
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    还需要配置,才能找到这个类,在app模块AndroidManifest.xml文件添加这段代码
    在这里插入图片描述

     <activity android:name="com.test.androidapp.UnityGameActivity"
                android:theme="@style/UnityThemeSelector"
                android:screenOrientation="userPortrait"
                android:launchMode="singleTask"
                android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
                android:hardwareAccelerated="false">
                <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
                <meta-data android:name="android.notch_support" android:value="true" />
     </activity>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后运行到自己的手机上就行了(不会运行的可以找找看android基础教程学习下)
    如果是游戏模拟器运行的话,先打开模拟器,再点击下图标识①按钮同步下gradle文件,再点击②运行按钮,就可以在模拟器上运行了。
    在这里插入图片描述
    运行结果如下:
    演示步骤:
    点击 进入UNITY->跳转到Unity界面同时传递数据显示在Unity中;
    点击Unity中的 ShowMainActivity(Quit)或者 ShowMainActivity(Unload)按钮返回APP首页并同时将Unity数据传递给TextView组件;
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    其他问题:
    1.NDK路径配置问题
    在这里插入图片描述
    项目的Local.proporties文件下添加

    我这里是用的Unity3D编辑器下载的NDK路径

    ndk.dir=自己ndk路径

    在这里插入图片描述
    2.构建出包出现两个APP图标
    找到unityLibrary模块的AndroidManifest.xml文件,删除里面的代码
    在这里插入图片描述
    3.从Unity返回其他APP页面闪退应用的问题
    操作:点击Exit按钮( Application.Quit();)
    app模块AndroidManifest.xml文件添加如下代码
    在这里插入图片描述

    android:process=":UnityActivity"
    
    • 1

    项目工程仓库地址

    参考文章:
    1.(Unity官方)Unity作为库的方式嵌入原生Android/IOS/tvOS案例
    2.Unity2020.2.6导出项目到Android Studio4.1.2中
    3.Android内嵌Unity并实现互相跳转的实例代码
    4.Unity游戏嵌入Android应用(融合为一个应用)

    十分感谢上述博客作者的帮助!~

  • 相关阅读:
    vue2技能树(12)-路由守卫、动态路由、状态管理
    户外运动耳机如何选择、最优秀的五款户外运动耳机推荐
    【Java】Jxls--轻松生成 Excel
    K8S之Pod详解
    2022/8/19 树莓派烧录与连接
    AOSP源码中Android.mk文件中的反斜杠符号(\)的作用和使用
    非零基础自学Java (老师:韩顺平) 第4章 运算符 4.12 进制 && 其他进制转十进制 && 十进制转其他进制
    odoo 开发入门教程系列-模块交互
    [论文阅读](Objective Quality Evaluation of Dehazed Images)
    java毕业生设计医院管理系统计算机源码+系统+mysql+调试部署+lw
  • 原文地址:https://blog.csdn.net/qq_40249982/article/details/127565349