Unity和Android的交互

Unity和Android的交互

  • 一、前言
  • 二、Android导出jar/aar包到Unity
    • 2.1 版本说明
    • 2.2 拷贝Unity的classes.jar给Android工程
      • 2.2.1 classes.jar的位置
      • 2.2.2 Android Studio创建module
      • 2.2.3 拷贝classes.jar 到 Android工程并启用
    • 2.3 编写Android工程代码
      • 2.3.1 创建 MainActivity
      • 2.3.2 MainActivity改继承 UnityPlayerActivity
      • 2.3.3 拷贝 UnityPlayerActivity
      • 2.3.4 编写Android工程代码
      • 2.3.5 编写AndroidManifest.xml
    • 2.4 编写Unity工程代码
      • 2.4.1 搭建游戏物体GameObject
    • 2.5 导出和构建APK
      • 2.5.1 导出Java文件
      • 2.5.2 导出Jar包
        • 2.5.2.1 构建Jar包
        • 2.5.2.2 新建Task任务构建和重命名Jar包
      • 2.5.3 导出aar包
        • 2.5.3.1 构建aar和修改aar包
        • 2.5.3.2 压缩aar包细节
        • 2.5.3.3 解决Androidmainfest合并问题
        • 2.5.3.4 解决桌面2个启动图标

一、前言

Unity和Android原生交互的方式大概有2种,一种是Android导出jar/aar包给Unity,然后Unity来构建apk;另外一种是Unity勾选Export Project,导出为一个Android工程,然后用AndroidStudio构建APK;这里讲讲第一种方式

二、Android导出jar/aar包到Unity

2.1 版本说明

  • 这里我用的Unity版本是 2020.3.33f1c2; AndroidStudio 用的版本是 2021.3.1
    思路大致是一致的,但不同版本可能导出的文件路径不太一样或内容可能不太一样,这个要注意一下

2.2 拷贝Unity的classes.jar给Android工程

2.2.1 classes.jar的位置

classes.jar 位于 {Unity 安装位置}\Editor\Data\PlaybackEngines\AndroidPlayer\Variations{mono或il2cpp}\Release\Classes\ 目录下

  • 这里主要涉及到2个变量,一个是Unity版本,一个是mono或il2cpp;
    不同Unity版本的安装路径不一样,可以用UnityHub点击 设置按钮-在资源管理中显示。快速进入到对应版本的目录;
    在这里插入图片描述
    至于mono或il2cpp,主要看你选用哪种打包方式。由于是示例空工程,并且要快速构建,所以我这里选了mono;

2.2.2 Android Studio创建module

先用Android Studio新建一个Android Studio空工程,接着我们创建一个module模块来实现功能
点击 File-New-New Module,拉起创建面板
在这里插入图片描述
注意,这里要先切换到Android Library页签,然后可以输入你喜欢的module名字
在这里插入图片描述

2.2.3 拷贝classes.jar 到 Android工程并启用

  • 拷贝第一步的classes.jar,并粘贴进模块的libs目录下
    在这里插入图片描述
  • 启用classes.jar
    粘贴进来后,其实还没起作用。右键classes.jar,然后点击 Add as Library
    在这里插入图片描述
    然后你会发现classes.jar有个三角标了,这时才起作用了。
    在这里插入图片描述
    Add as Library 这一步操作等同于在module的build.gradle里添加 implementation files(‘libs\classes.jar’), 然后 点击 File - Sync Project with Gradle Files按钮;
    我们也可以通过修改文件的方式来快速的添加和移除lib
    在这里插入图片描述

2.3 编写Android工程代码

2.3.1 创建 MainActivity

在模块下创建MainActivity ,也不一定要这个名字,可以根据你喜欢的来
在这里插入图片描述

2.3.2 MainActivity改继承 UnityPlayerActivity

这里MainActivity 改继承自UnityPlayerActivity,不出意外,改继承后这里会报红。这是因为新版本的Unity(大概是2019和之后的版本),导出的 classes.jar 不再包含 UnityPlayerActivity了。所以我们需要再拷贝一个UnityPlayerActivity过来
在这里插入图片描述

2.3.3 拷贝 UnityPlayerActivity

这个路径在对应Unity版本的 Editor\Data\PlaybackEngines\AndroidPlayer\Source\com\unity3d\player目录下。只要找到前面的classes.jar,相信这个对你来说也是轻松找到
在这里插入图片描述

  • 拷贝进Android工程
    如下图,可以保持和MainActivity同级,拷贝进来
    在这里插入图片描述
    然后修改package路径(可以从MainActivity.java里拷贝package路径粘贴进来)和 引入命名空间
    代码如下,熟悉的朋友也可以在报红处按下Alt + Enter,手动引入
package com.example.linkunitylibrary; //这里拷贝你MainActivity的package
//import导入依赖
import com.unity3d.player.IUnityPlayerLifecycleEvents;
import com.unity3d.player.MultiWindowSupport;
import com.unity3d.player.UnityPlayer;

然后 MainActivity就不报红了
在这里插入图片描述

  • 题外话
    记得好像有些教程是放在com.unity3d.player目录下,并保持原包名,如下图的层级和package路径。我有测试过,但在最后构建apk会报错。
    在这里插入图片描述
  • 原因我觉得是这样:因为最终我们要把android studio构建出来的jar包或aar包,放回unity。那如果这里保持一样的包名,到时放回unity,就相当于有2个一样的 com.unity3d.player.UnityPlayerActivity 类了,那就会报错。所以我觉得这种方式应该不可行…

2.3.4 编写Android工程代码

这里我们分别 写一个方法给Unity调用 和 调用Unity里的一个方法
在这里插入图片描述
代码如下

package com.example.linkunitylibrary;
import android.os.Bundle;
import com.unity3d.player.UnityPlayer;

/**
 * Created by super41 on 2024/4/5.
 */
public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //调用Unity的方法
        a2u_add(1,5);
    }

    //写一个方法给Unity调用
    public int u2a_add(int a,int b){
        return a + b;
    }

    //调用unity里的方法
    public void a2u_add(int a,int b){
        sendMsg2Unity("a2u_add",a+"|"+b);
    }

    private static final String LinkGameObjectName  = "UnityLinkAndroidGo";
    /**
     * 给Unity发消息
     * @param methodName 方法名
     * @param params 参数
     */
    private void sendMsg2Unity(String methodName,String params){
        //依次传入 GameObjectName, GameObject里的方法,参数
        //因为这里 GameObjectName 一般固定,所以封装起来
        //只能传递一个参数,多个参数拼接在params里
        UnityPlayer.UnitySendMessage(LinkGameObjectName,methodName,params);
    }
}

2.3.5 编写AndroidManifest.xml

修改AndroidManifest.xml,这里记得替换为你自己的包名和类名

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:supportsRtl="true">
        <activity android:name="com.example.linklibrary.MainActivity" android:exported="true"> <!--包名和类名,这里换成你自己的包名和类名-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" /> <!--主入口Activity-->
                <category android:name="android.intent.category.LAUNCHER" /> <!--添加到桌面-->
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>

</manifest>

2.4 编写Unity工程代码

2.4.1 搭建游戏物体GameObject

  • 创建2个Text文本,一个显示Unity_Call_Android的加法结果,一个显示Android_Call_Android的加法结果
    在这里插入图片描述
  • 创建UnityLinkAndroidGo物体,这里游戏名字要跟Android代码里的物体名字一致。
    创建任意名字的脚本,如UnityLinkAndroidScript,代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UnityLinkAndroidScript : MonoBehaviour
{

    public Text Txt1;
    public Text Txt2;
    
    // Start is called before the first frame update
    void Start()
    {
        u2a_add(100,200);
    }

    /// <summary>
    /// Unity调用Android
    /// </summary>
    void u2a_add(int a, int b)
    {
        //标准操作,获取MainActivity对象
        AndroidJavaClass javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject javaObject = javaClass.GetStatic<AndroidJavaObject>("currentActivity");
        //调用方法
        var result = javaObject.Call<int>("u2a_add", a, b);
        Txt1.text = $"UnityCallAndroid {a} + {b} = {result}";
    }

    /// <summary>
    /// 给android调用的方法
    /// </summary>
    /// <param name="objs"></param>
    void a2u_add(string objs)
    {
        var arrays = objs.Split('|');
        var a = int.Parse(arrays[0]);
        var b = int.Parse(arrays[1]);
        var result = a + b;
        Txt2.text = $"AndroidCallUnity {a} + {b} = {result}";
    }
    
}

然后把2个Text附上
在这里插入图片描述

2.5 导出和构建APK

一切准备就绪,接下来就是导出和构建APK了

2.5.1 导出Java文件

我们一般是导出jar包和aar包文件。其实也可以用导出java文件的方式
如下图,3个文件导出到Unity的Assets/Plugins/Android/目录下,如没有目录则创建
在这里插入图片描述
在这里插入图片描述
接着就是常规的构建apk了。我们来看运行结果。可以看到成功运行了起来
在这里插入图片描述

2.5.2 导出Jar包

2.5.2.1 构建Jar包

上面的导出java文件一般不是主流的方式,还是导出成一个压缩包的形式更好管理一点。我们来看看怎么导出jar包
选中你的module,然后点击 Build - Make Module xxx. 然后打开资源管理器,就可以看到这里生成了一个build目录
在这里插入图片描述
进入build\intermediates\aar_main_jar\debug\目录(这里不同Android版本可能输出路径不一样,思路是一致的)
可以找到classes.jar
在这里插入图片描述
拷贝这里的classes.jar拷贝到Plugins/Android/目录下,并删除掉原来的java文件。如下图
在这里插入图片描述
然后就是构建apk了,也可以得到我们想要的效果;

2.5.2.2 新建Task任务构建和重命名Jar包
  • 一般我们需要重命名我们的jar包,不然叫classes.jar不太好,手动改每次都要来一遍不太好。我们可以添加task任务来实现这一步骤。打开module的build.grade,在文件的最后添加任务

在这里插入图片描述
代码如下:

//删除旧jar包
task tryDeleteOldJar(type:Delete){
    delete('release/HelloWorld.jar')
}

//导出并重命名jar包
task exportJar(type : Copy){
    from('build\\intermediates\\aar_main_jar\\debug')//从这个路径拷贝
    into('release') //拷贝到这个路径
    include('classes.jar')
    rename('classes.jar','HelloWorld.jar')
}

//添加依赖
exportJar.dependsOn(tryDeleteOldJar,build)

然后Sync刷新一下
在这里插入图片描述
点击右侧的Gradle,然后点击Task/other
在这里插入图片描述
就可以看到exportJar任务了
在这里插入图片描述
双击执行它,我们可以得到一个重命名好的HelloWorld.jar
在这里插入图片描述
如果看不到Task任务,那是一些版本的AndroidStudio隐藏掉了,可以进入File-Setting-Experimental,然后取消勾选这个Do not build… 选项,然后点击OK后你就可以看到Task了(当然不同版本可能设置不一样,这里主要介绍一种解决思路)
在这里插入图片描述
到此,导出Jar包的形式也好了

2.5.3 导出aar包

我们还可以导出aar包,aar和jar包的区别是,jar包一般只包含java文件,而aar包除此之外,还可以包含res和AndroidMainfest.xml

2.5.3.1 构建aar和修改aar包

接下来我们来实现导出aar包;
和之前一样,我们选中Module,点击Build-Make Module来构建
在这里插入图片描述
可以在build/outputs/aar里面找到aar
在这里插入图片描述
aar包本质上是一个压缩包,我们改为.zip格式,然后用压缩文件打开。目录大致如下
在这里插入图片描述
还记得我们从unity拷贝了一个classes.jar过来嘛,这个文件也会被构建进aar包,然后当我们把aar包放回unity,那么就会有重名的类。所以我们需要删除掉这个classes.jar
在这里插入图片描述
具体的操作方式是:
把libs里的classes.jar删掉,然后箭头的classes.jar拖进libs目录
在这里插入图片描述

2.5.3.2 压缩aar包细节

然后接下来就是重新压缩成一个aar包了。压缩这里有2点很关键,我当时自己也是踩坑了好几个小时。
现在说一下:

  • 选中你的文件列表,然后右键进行压缩,如下图。注意不要在外层目录压缩,不然会多嵌套一层文件夹,导致Unity识别不到;
    在这里插入图片描述
  • 压缩格式这里选zip,不要选默认的rar… 当时就是选rar,然后就进坑了…
    在这里插入图片描述
    然后把得到的压缩包改为aar后缀
    在这里插入图片描述
    然后放回Unity,因为aar包已经有了AndroidMainfest.xml,所以可以把之前的删掉,只留下aar包
    在这里插入图片描述
    接下来就是构建出APK了,也可以得到我们想到的效果;
2.5.3.3 解决Androidmainfest合并问题

如果构建过程中提示AndroidMainfest 合并失败,如下图。是因为最小sdk版本要改一下
在这里插入图片描述
这里你可以对aar包里的minSdkVersion进行修改,改成提示的版本,比如我这里是改成19,然后导出为压缩包
在这里插入图片描述
也可以对Modudle的build.gradle改下minsdk版本,我这里是改成19,这种方式更好一点。然后重新构建aar包
在这里插入图片描述

2.5.3.4 解决桌面2个启动图标

使用导出aar的方式构建出的apk,桌面上可能会有2个程序入口,也就是2个图标。
我们需要修改下AndroidMainfest.xml
进入PlayerSetting,勾选 CustomMainfest.xml
在这里插入图片描述
然后会自动生成AndroidMainfest.xml
在这里插入图片描述
进入AndroidMainfest.xml,把这个activity整段删掉,然后再构建apk即可
在这里插入图片描述

最后导出APK就可以了。至此我们就完成了3种导出方式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot之mybatisPlus多表查询及分页查询

文章目录 一、多表查询二、mybatis-plus条件查询三、分页查询 一、多表查询 可能会用到的注解 这里的场景是&#xff0c;查询每个用户及其所有的订单。就是查询你的id号的同时&#xff0c;把你所有的历史订单信息都拉出来。 表结构这样 CREATE TABLE User ( id INT PRIMARY…

docker笔记(一):安装、常用命令

一、docker概述 1.1docker为什么会出现 各种环境配置十分繁琐&#xff0c;每一个机器都需要配置环境&#xff0c;难免出现各种问题。 发布一个项目jar需要配置&#xff08;MySQL、redis、jdk、…&#xff09;&#xff0c;项目不能都带上环境安装打包&#xff1a; 传统&…

PostgrerSQL基本使用与数据备份

前言 上篇了解了 PostgrerSQL 数据库的部署PostgreSQL关系型数据库介绍与部署-CSDN博客&#xff0c;本篇将继续就其基本操作、备份与还原内容做相关介绍。 目录 一、数据库的操作 1. 本机登录 2. 开启远程登录 2.1 开放远程端口 2.2 编辑配置文件 2.3 修改配置密码 2.…

前端三剑客 —— JavaScript (第一天)

目录 回顾内容 1.弹性布局 2.网格布局 JavaScript 概述 发展 浏览器 什么是Javascript JavaScript 能干什么 JavaScript需要的环境 JavaScript初体验 基本数据 JS书写方式 行内JS 页面JS 外部JS 1&#xff09;创建外部JS文件 2&#xff09;编写页面 对话框 警…

【踩坑日记】因不同系统换行符不同导致的文本读取结果不同的问题

文章目录 1 问题现象描述2 解决过程&#xff08;点击直接跳到解决方法&#xff09;3 原因解释4 如何避免踩坑4.1 格式转换4.2 格式查看 1 问题现象描述 起因是群友问了这么一个问题 确实很奇怪&#xff0c;按理说第二个printf不会完全不输出&#xff0c;于是想到&#xff0c;…

C++数据结构与算法——回溯算法组合问题

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

SD-WAN如何解决更有性价比地跨境网络问题

云桥通SD-WAN利用智能路由和负载均衡技术&#xff0c;优化数据传输路径&#xff0c;提高网络性能和可靠性。这意味着数据在跨国传输时可以更快到达目的地&#xff0c;减少延迟和丢包率。跨境SD-WAN提高了网络连接速度和质量&#xff0c;使用户能够更快地访问跨国业务所需的资源…

索引的概念

索引的概念    1.索引是一种可选的与表相关的数据库对象&#xff0c;用于提高数据的查询效率。    2.索引是一种有序的数据结构。    3.如果一个表没有创建索引&#xff0c;则对该表进行查询时需要进行全表扫描&#xff1b;如果创建了索引&#xff0c;则在有条件查询时…

应用性能分析工具CPU Profiler

简介 本文档介绍应用性能分析工具CPU Profiler的使用方法&#xff0c;该工具为开发者提供性能采样分析手段&#xff0c;可在不插桩情况下获取调用栈上各层函数的执行时间&#xff0c;并展示在时间轴上。 开发者可通过该工具查看TS/JS代码及NAPI代码执行过程中的时序及耗时情况…

福州装修答疑 | 飘窗能不能砸掉?福州中宅装饰,福州装修

装修中的飘窗是一种常见的装饰元素&#xff0c;它不仅可以增加室内的采光和通风效果&#xff0c;还能为居室增添一份雅致和温馨。然而&#xff0c;很多业主在装修中都会遇到一个共同的问题&#xff1a;装修中的飘窗到底能不能砸&#xff1f;什么情况下可以砸&#xff1f;什么情…

IO流【带有缓冲区的字节输入、输出流;字符输入、输出流 转换流】

day35 学习注意事项 按照流的发展历史去学习注意流与流之间的继承关系举一反三 IO流 继day36 字节流继承图 字节流 应用场景&#xff1a;操作二进制数据&#xff08;音频、视频、图片&#xff09; abstract class InputStream – 字节输入流的基类&#xff08;抽象类&#xff0…

基于R、Python的Copula变量相关性分析及AI大模型应用

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

Anaconda环境命令样例

启动命令行Anaconda Powershell Prompt 查看环境列表 (base) PS C:\Users\Administrator> conda env list # conda environments: # base * G:\ProgramData\anaconda3 MoneyprinterTurbo G:\ProgramData\anaconda3\envs\MoneyprinterTurbo pytorc…

C++ 标准库类型stackqueue

C/C总述&#xff1a;Study C/C-CSDN博客 栈与队列详解&#xff08;数据结构&#xff09;&#xff1a;栈与队列_禊月初三-CSDN博客 stack&#xff08;栈&#xff09; stack的常用函数 函数说明功能说明stack()构造空栈push(T& val)将元素val压入栈中size()返回栈中元素个…

数据结构之二叉树由浅入深最终章!

题外话 我说清明节想放松一下没更新大家信吗? 博客毕竟是文字不是视频,大家如果有不明白的地方,可以使用数形结合的方式,画图一边通过图片,一边通过对照代码进行推导一下,有什么问题都可以私信我或者写在评论区 正题 第一题 寻找二叉树中p,q最近公共祖先 第一题思路 先…

【C++】红黑树讲解及实现

前言&#xff1a; AVL树与红黑树相似&#xff0c;都是一种平衡二叉搜索树&#xff0c;但是AVL树的平衡要求太严格&#xff0c;如果要对AVL树做一些结构修改的操作性能会非常低下&#xff0c;比如&#xff1a;插入时要维护其绝对平衡&#xff0c;旋转的次数比较多&#xff0c;更…

【Claude 3】This organization has been disabled.此组织已被禁用。(Claude无法对话的原因和解决办法)

Claude对话提示 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 问题截图 问题原因 出现该页面&#xff0c;表示您的账户已经无法使用&#xff0c;可能…

顺序表相关习题

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

Jenkins (三) - 拉取编译

Jenkins (三) - 拉取编译 通过Jenkins平台 git 拉取github上项目&#xff0c;通过maven编译并打包。 Jenkins 安装 git 插件 Manager Jenkins -> Plugins -> Available plugins -> Git 打包编译检验 FressStyle 风格编译 New Item输入 item name Spring-Cloud-1…

回溯法(一)——全排列 全组合 子集问题

全排列问题 数字序列 [ l , r ] [l,r] [l,r]​区间内元素的全排列问题 extern int ans[],l,r,num;//num&#xff1a;方案数 extern bool flag[]; void dfs(int cl){//cl:current left&#xff0c;即为当前递归轮的首元素if(cl r 1){//数组已越界&#xff0c;本轮递归结束for…