Unity通过NDK实现C#与C++之间的相互调用

news2024/12/29 11:01:26

由于一些历史遗留问题,我们项目还在使用一套C++实现的Box2D定点数的库,由于最近修改了视野算法所以需要重新打包安卓的【.so】文件,特此记录
1、关于NDK
在Android平台,C/C++需通过NDK编译成动态链接库.so文件,然后C#中通过[DllImport(“soname”)]声明后即可调用。(我们项目使用了lua作为胶水层进行调用 原理一样但是需要单独打一个lua的so文件 在安卓平台上)windows 平台运行dll即可

C#是Unity的官方推荐的开发语言。如果某些逻辑需要C++支持以提供高性能特性,或者需要C++去跟底层硬件或者操作系统级别的接口交互,那么就需要用C#去调用C++的接口。
这往往依赖动态链接。即用C++开发动态链接库,然后C#调用动态链接库里暴露的接口。

而在Android上开发动态链接库(.so文件)的方法,就是NDK开发。
NDK,全称Native Development Kit,是Android的一种开发工具包。
目前的Android开发,不再是纯粹的Java层开发,更多的会与C/C++结合,把一些重要的方和行为以及一些私密性质的东西放在C/C++中,通过NDK将其编译成.so动态库文件,放入工程中的libs目录。
2.版本
注意需要下载的版本。如果你的NDK同时也为Unity的IL2cpp服务,那就需要注意Unity对NDK的版本有硬性要求,比如2018的版本要求 NDK r16b的版本
下载地址
在这里插入图片描述
下载下来的压缩包,解压之后会得到如下目录:
在这里插入图片描述
最重要的是那个叫做 ndk-build.cmd 的脚本文件。它是一个基于make的构建脚本。查看它的源码你会发现它实际上执行的是:
在这里插入图片描述
为了能够在全局使用这个脚本,你最好将这个目录加入到环境变量中,也可以在这个目录打开cmd 看个人习惯
3.利用NDK构建.so
现在开始利用NDK构建.so文件,目的是在.so文件中暴露一个函数给C#调用。这个函数是自定义的加法函数,它会将两个整型相加,再返回结果。

class  MyClass
{
	public:
    static float AddFloat(float a, float b)
    {
        return a + b;
    }
 
};


extern "C"
{
	float AddFloat(float a,float b)
	{
		return MyClass::AddFloat(a,b);
	}
}

我们将在这个文件所在的目录下运行ndk-build.cmd,在此之前,我们还需要两个文件:Android.mk 和 Application.mk。这两个文件将包含 ndk-build 所需要的所有配置。两个文件的内容如下:
Android.mk

LOCAL_PATH := $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_MODULE    := gh_so
LOCAL_SRC_FILES := \
cs_call_c.c \
cs_call_cpp.cpp \
cpp_call_cs.h \
cpp_call_cs.cpp \
java_call_cpp.cpp \
cpp_call_java.cpp
 
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_STL := c++_static
APP_CPPFLAGS := -frtti -std=c++11
APP_PLATFORM := android-19
APP_CFLAGS += -Wno-error=format-security
APP_BUILD_SCRIPT := Android.mk
APP_ABI := armeabi-v7a x86

Android.mk 为 ndk-build描述了构建所需的源代码,链接库等信息。

变量描述
LOCAL_PATH这个变量描述了构建所需的源代码在当前工程中的相对位置。如果你是使用Android Studio进行开发,那源代码一般位于工程的 app/src/main/cpp 目录下。$(call my-dir) 返回Android.mk所在的目录。
CLEAR_VARS将一个特殊的Makefile文件包含进来。CLEAR_VARS定义了这个文件的位置。这个Makefile文件会清除所有之前定义过的Android.mk变量,一般都以LOCAL_开头
LOCAL_MODULEndk-build构建出来的库的名称,如果设定的名称没有以lib开头,则ndk-build会自动为你加这个前缀。例如,上面定义的名称将构建出来一个叫gh.so的库
LOCAL_C_INCLUDES当你的代码中include一些文件不在默认的搜索路径时,你可以使用这个变量去指定。一般来说,标准c头文件,和一些常见的Android头文件,都是在默认的搜索路径中的,他们都可以在你下载的NDK工具包中找得到
LOCAL_SRC_FILES需要构建的源文件(不包含头文件)都需要放到这里面
LOCAL_CFLAGS这个变量定义一些ndk-build的编译选项,一般来说,不需要额外的设置
BUILD_SHARED_LIBRARY将一个特殊的构建文件包含进来。BUILD_SHARED_LIBRARY定义了这个文件的位置。该文件会收集你在定义的所有上述LOCAL_开头的变量所包含的信息,然后确定如何从源文件中构建动态链接库。这个构建脚本要求你至少定义了LOCAL_MODULE和LOCAL_SRC_FILES两个变量。

Application.mk 为ndk-build描述了项目的构建性质。

|APP_STL | 需要使用哪些C++标准库 |
|APP_CPPFLAGS | 项目中,C++部分使用的编译选项 |
|APP_PLATFORM | 构建的Android API级别,对应于应用程序的min SDK Version |
|APP_CFLAGS | 项目中,C/C++部分使用的编译选项 |
|APP_BUILD_SCRIPT | Android.mk 文件所在的位置 |
|APP_ABI | 需要为哪些abi机器构建共享库 |

然后我们在终端输入以下命令:

ndk-build.cmd NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk

在这里插入图片描述
你会得到一个libs目录和一个obj中间文件暂存目录。libs目录里面就是对应你所设置的abi平台对应的两种架构的so文件
在这里插入图片描述

将so拷贝到Unity工程中的Assets/Plugins/Android/libs目录中
将我们打包出来的.so文件放到上述的目录下。因为不同abi的共享库名称都一样,为避免冲突,我们拷贝它的任意父目录即可。无论.so存放到哪儿,Unity都会识别出这个共享库属于哪个abi平台的。项目目录结构如下
在这里插入图片描述
4.通过DllImport调用

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;

public class CsCallCCPP_DllImport_SO : MonoBehaviour
{
    public Text txt;
    public Button btn1;
    public Button btn2;

    void Start()
    {
        btn1.onClick.AddListener(() => 
        {
            //C#调用C
            txt.text = "C#调用C接口: AddInt(2, 6) = " + AddInt(2, 6);
        });

        btn2.onClick.AddListener(() => 
        {
            //C#调用C++
            txt.text = "C#调用C++接口: AddFloat(2.7, 4.2) = " + AddFloat(2.7f, 4.2f);
        });


    }

    //C接口
    [DllImport("gh_so")]
    public static extern int AddInt(int a, int b);

    //C++接口
    [DllImport("gh_so")]
    public static extern float AddFloat(float a, float b);
}

5.打包调用
在这里插入图片描述

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

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

相关文章

MATLAB基础应用精讲-【数模应用】分层聚类(附python代码实现)

目录 前言 知识储备 层次聚类 1. 算法解读: 2. 步骤和细节: 3. 举例: 4. 算法评价: 5. 算法的变体: 算法原理 基本思想 分层聚类网络的原理 分层聚类网络的优势 分层聚类网络的应用领域 SPSSAU 分层聚类案例 1、背景 2、理论 3、操作 4、SPSSAU输出结果…

STM32智能机器人导航系统教程

目录 引言环境准备智能机器人导航系统基础代码实现:实现智能机器人导航系统 4.1 数据采集模块 4.2 数据处理与导航算法 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景:机器人导航应用与优化问题解决方案与优化收尾与总结 1. 引言 智能机器…

如何找到关于目标检测小论文的创新点

深度学习目标检测的小论文创新点 数据集预处理创新 主要包括图像增强、图像去雾、图像融合和图像降噪 例子: 比如在研究方向是检测晚上或者天气不好时骑电动车的人是否佩戴了安全头盔。一般的检测可能只能检测到正常天气情况下的骑电动车的人,而对于大雾天气和晚上…

LT7911UX 国产原装 一拖三 edp 转LVDS 可旋转 可缩放

2.一般说明 该LT7911UX是一种高性能Type-C/DP1.4a到MIPI或LVDS芯片的VR/显示应用。HDCP RX作为HDCP转发器的上游,可以与其他芯片的HDCP TX配合实现转发器功能。 对于DP1.4a输入,LT7911UX可配置为1/2/4通道。自适应均衡使其适用于长电缆应用,最…

基于单片机的空调控制器的设计

摘 要 : 以单片机为核心的空调控制器因其体积小 、 成本低 、 功能强 、 简便易行而得到广泛应用 。 本设计通过 AT89S52 控制DS18&a…

电商项目中分与元金额单位互转实战

在Java开发中,可能遇到金额单位的转换,比如本系统用分作为金额的基本单位,对方系统用元作为金额的基本单位,这就需要进行单位转换,记录下来,方便备查。 一、分转元 分转元,分到元相差两位&…

单片机软件架构连载(5)-队列

前面讲了指针、结构体之类的基础知识。 这篇内容开始,就要对这些基础知识,做一些复杂的应用了,比如说队列。 其实,在2018年的时候,我录制过一套程序架构的视频,里面有手把手写队列的教程,讲了一…

js逆向研究【案例实战2】

接口分析 数据接口如下: 没有关键词,选择使用接口路径定位js【使用路径api/coin/tickers】 定位到一下js文件,并在来源面板打开。 逆向调试与代码还原 搜索关键词JSON.parse 定位到以上的位置,断点调试后确定er函数即为解密…

CC2利用链分析

分析版本 Commons Collections 4.0 JDK 8u65 环境配置参考JAVA安全初探(三):CC1链全分析 分析过程 CC2是在CC4的基础上做了一点改动,和之前CC3结合CC1 InvokerTransformer一样的。CC3利用链分析 因为TemplatesImpl是可序列化的,利用反射把Template…

AI工具,如何通过 GPT-4o 提高工作效率

文章目录 引言一、理解GPT-4o及其功能二、如何利用GPT-4o提高工作效率1. 代码生成与优化2. 自动化测试与调试3. 技术文档撰写与知识管理 三、实际案例与成功应用1. GitHub 协作与问题解决2. 敏捷开发与迭代优化 四、GPT-4o的挑战与应对策略五、未来展望与发展方向六、结论 &…

开发任务优先级排序 6大影响因素

开发任务优先级排序,有助于项目关键任务获得充足资源,确保项目关键路径不受阻碍,助力项目按时完成,减少后期风险和不确定性。如果没有对开发任务进行优先级排序,团队可能会花费大量时间在低价值或非关键任务上&#xf…

昇思MindSpore学习入门-模型模块自定义

基础用法示例 神经网络模型由各种层(Layer)构成,MindSpore提供构造神经网络层的基础单元Cell,基于Cell进行神经网络封装。下面使用Cell构造经典模型AlexNet。 如图所示,AlexNet由5个卷积层与3个全连接层串联构成,我们使用mindspo…

Open3D 从体素网格构建八叉树

目录 一、概述 1.1体素网格 1.2八叉树构建 1.3应用 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2体素网格 3.3八叉树 3.4体素网格 一、概述 八叉树(Octree)是一种树状数据结构,用于递归地将三维空间划分为…

Docker进入MongoDB

先是命令行开启docker镜像,然后进入docker镜像,这是两步 进入之后,开头会变成root,我的理解是进入了另一个linux系统了,直接执行相应的软件 这里直接use databse就是进入了,据说MongoDB是慢启动&#xff0c…

2.5 C#视觉程序开发实例1----CamManager实现模拟相机采集图片(Form_Vision部分代码)

2.5 C#视觉程序开发实例1----CamManager实现模拟相机采集图片(Form_Vision部分代码) 1 目标效果视频 CamManager 2 增加一个class IMG_BUFFER 用来管理采集的图片 // <summary> /// IMG_BUFFER 用来管理内存图片的抓取队列 /// </summary> public class IMG_BUFF…

基于AI的3D场景重建技术新突破!破局自动驾驶端到端仿真

场景的保真度&#xff0c;对于自动驾驶仿真和合成数据生成至关重要&#xff0c;场景重建保真度不足&#xff0c;将极大地制约高阶自动驾驶系统的训练效果。同时&#xff0c;在用于训练大模型和具身智能的合成数据领域&#xff0c;同样对低成本的高保真场景有巨大的需求。 在此…

【Python_GUI】thinker布局管理——pack()方法

pack()方法是比较常用的布局组件之一&#xff1a;其语法如下&#xff1a; widget.pack(options) 其常用的参数及含义如下&#xff1a; 参数含义side设置组件水平展示或垂直展示padx设置组件距离窗口的水平距离pady设置组件距离窗口的垂直距离ipadx设置组件内的文字距离组件边…

从零开始做题:My_lllp

题目 给出一张png图片 解题 ┌──(holyeyes㉿kali2023)-[~/Misc/题目/zulu/My_lllp] └─$ python2 lsb.py extract my_lllp.png out.txt my_lllp [] Image size: 1080x1079 pixels. [] Written extracted data to out.txt. ┌──(holyeyes㉿kali2023)-[~/Misc/题目/zul…

HSP_15章 Python_模板设计模式和oop进阶总结

P136 模板设计模式 1. 设计模式简介 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式 设计模式就像是经典的棋谱&#xff0c;不同的棋局&#xff0c;我们用不同的棋谱&#xff0c;免去我们自己再思考和摸索 2. 模板设计模式 基本…

DFS回溯剪枝|KMP通过数组记录减少判断子字符串|思路

KMP|DFS回溯剪枝 #1、NC149kmp 初步思路&#xff1a; 两层for循环&#xff0c;一个T的字符开始与 S的字符比较&#xff0c;挨个比较&#xff0c;遇到不同就continue当前T的字符&#xff0c;重复步骤》效率太低&#xff0c;超时 eg: TABSABABABD SABABD S&#xff01;A时&#…