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

由于一些历史遗留问题,我们项目还在使用一套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.cppinclude $(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#调用Ctxt.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.mzph.cn/pingmian/43242.shtml

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

相关文章

大量数据渲染怎么优化速度

1. 分页加载 将数据分成若干份,每次请求当前页数据,在触底加载更多或者点击分页时加载下一页数据。 2. 虚拟列表 只渲染当前视口的数据,当用户滚动时动态更新视口里的内容,并不是一次渲染整个列表,这个方法比较适用…

beanstalkd安装配置方法

目录 概述特性比较Ubuntu下安装示例启动 Beanstalkd 服务查看 Beanstalkd 服务状态重启 Beanstalkd 服务停止 Beanstalkd 服务配置beanstalkd 持久化参考资料 概述 Beanstalkd 是一个简单、快速、轻量级的开源消息队列系统,用来处理异步任务和消息传递。适合需要引…

播放ReadableStream格式二进制流音频

播放ReadableStream格式二进制流音频 接口返回中&#xff0c;body为ReadableStream格式的二进制流 <!DOCTYPE html> <html><head><title>实时语音生成与播放</title></head><body><h1>输入文本生成语音</h1><textare…

【渗透测试】利用hook技术破解前端JS加解密 - JS-Forward

前言 在做渗透测试项目时&#xff0c;尤其是金融方面&#xff0c;经常会遇到前端JS加解密技术&#xff0c;看着一堆堆密密麻麻的密文&#xff0c;会给人一种无力感。Hook技术则会帮助我们无需获取加解密密钥的前提下&#xff0c;获取明文进行渗透测试 环境准备 JS-Forward Burp…

城市地下综合管廊物联网远程监控

城市地下综合管廊物联网远程监控 城市地下综合管廊&#xff0c;作为现代都市基础设施的重要组成部分&#xff0c;其物联网远程监控系统的构建是实现智慧城市建设的关键环节。这一系统集成了先进的信息技术、传感器技术、通信技术和数据处理技术&#xff0c;旨在对埋设于地下的…

DangerWind-RPC-framework---一、服务注册与发现

服务的注册与发现借助Spring Bean的生命周期来完成。 Spring Bean的生命周期的步骤中包含以下几步&#xff1a;调用aware接口、BeanPostProcessor 执行postProcessBeforeInitialization方法、BeanPostProcessor 执行postProcessAfterInitialization方法。现在需要利用这三个步骤…

动态模型管理:Mojo模型的自定义保存与加载控制

动态模型管理&#xff1a;Mojo模型的自定义保存与加载控制 在机器学习模型的生命周期中&#xff0c;模型的保存与加载是一个至关重要的环节。Mojo模型&#xff0c;作为H2O.ai提供的一种模型部署格式&#xff0c;主要用于模型的序列化和预测。Mojo模型支持将训练好的模型转换为…

sql 清空表,并清空自增 id

执行 sql TRUNCATE 表名 表名替换为自己要清空的表 在 Navicat 中 新建查询输入 上述 sql点击运行即可表页 f5 刷新&#xff0c;数据已经清空&#xff0c;再次新增数据&#xff0c;自增 id 从 1 开始

Tomcat的负载均衡、动静分离

一、如何tomcat和nginx负载均衡及动静分离&#xff1a;2台tomcat&#xff0c;3台nginx来实现 1.首先设置tomcat1和tomcat2服务器 关闭两台tomcat的防火墙及安全机制&#xff1a;systemctl stop filwalld setenforce 0 进入tomcat目录的webapps中&#xff0c;创建test 2.配…

音频demo:使用opencore-amr将PCM数据与AMR-NB数据进行相互编解码

1、README a. 编译 编译demo 由于提供的.a静态库是在x86_64的机器上编译的&#xff0c;所以仅支持该架构的主机上编译运行。 $ make编译opencore-amr 如果想要在其他架构的CPU上编译运行&#xff0c;可以使用以下命令&#xff08;脚本&#xff09;编译opencore-amr[下载地…

【SpringBoot3】使用os-maven-plugin为项目自动添加常用的变量

一、什么是os-maven-plugin os-maven-plugin 是一个 Maven 扩展/插件&#xff0c;它根据 ${os.name} 和 ${os.arch} 生成各种有用的、与平台相关的项目属性&#xff0c;并将这些属性标准化。${os.name} 和 ${os.arch} 在不同的 JVM 和操作系统版本之间往往存在细微的差异&…

移除元素合并两个有序数组-LeetCode

一、移除元素 . - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; int removeElement(int* nums, int numsSize, int val) {int src0;int dst0;while(src<numsSize){if(nums[src]val){src;}else if (nums[src]!val){nums[dst]nums[src];src;dst;}}return dst…

渲染引擎之ECS架构介绍

1.什么是ECS&#xff1f; 我们先简单地介绍一下什么是ECS&#xff1a; E -- Entity 实体&#xff0c;本质上是存放组件的容器C -- Component 组件&#xff0c;引擎所需的所有数据结构S -- System 系统&#xff0c;根据组件数据处理逻辑状态的管理器 ECS全称Entity-Component-…

SAPUI5基础知识11 - 组件配置(Component)

1. 背景 组件&#xff08;Component&#xff09;是SAPUI5应用程序中独立且可重用的部件。 SAPUI5提供以下两类组件: faceless组件 (class: sap.ui.core.Component): 无界面组件即没有用户界面相关的元素&#xff0c;用于不需要UI元素编码的场景&#xff1b; UI组件 (class: …

C# 实现基于exe内嵌HTTPS监听服务、从HTTP升级到HTTPS 后端windows服务

由于客户需要把原有HTTP后端服务升级为支持https的服务&#xff0c;因为原有的HTTP服务是一个基于WINDOWS服务内嵌HTTP监听服务实现的&#xff0c;并不支持https, 也不像其他IIS中部署的WebAPI服务那样直接加载HTTPS证书&#xff0c;所以这里需要修改原服务支持https和服务器环…

每日复盘-20240708

今日关注: 20240708 六日涨幅最大: ------1--------300391--------- 长药控股 五日涨幅最大: ------1--------300391--------- 长药控股 四日涨幅最大: ------1--------300391--------- 长药控股 三日涨幅最大: ------1--------300391--------- 长药控股 二日涨幅最大: ------…

JAVA进阶学习11

文章目录 一、方法引用1.1 引用静态方法1.2 引用成员方法1.3 引用构造方法1.4 方法引用的其他调用方式1.4.1 使用类名引用成员方法1.4.2 引用数组的构造方法 1.5 总结二、异常2.1 异常的处理2.1.1 JVM虚拟机处理异常2.1.2 try...catch异常处理 2.2 异常中的常见方法2.3 异常的抛…

Java集合升序降序、转Set的方法

Collections.sort(list,Comparator.comparing(OcApplySquareVo::getApplyName).reversed()); 集合转set /** 集合转set */Set<String> pkCodeSet rows.stream().map(RailwayWeighBookResult.RailwayWeighBook::getPkCode).collect(Collectors.toSet());

互联网接入技术的简单介绍

引言 要连接到互联网&#xff0c;用户必须先连接到某个ISP&#xff08;互联网服务提供商&#xff09;。接入技术解决的就是用户如何连接到本地ISP的问题&#xff0c;通常称之为“最后一公里”。本文将详细介绍几种主要的互联网接入技术&#xff0c;帮助初学者了解不同的接入方…

【SOM神经网络的数据分类】SOM神经网络的数据分类的一个小案例

【SOM神经网络的数据分类】SOM神经网络的数据分类的一个小案例 注&#xff1a;本文仅作为自己的学习记录以备以后复习查阅 一 概述 自组织特征映射网络&#xff08;Self-Organizing Feature Map, SOM&#xff09;也叫做Kohonen网络&#xff0c;它的特点是&#xff1a;全连接、…