跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

c++ unity native plugin c++如何为unity编写原生c++插件

Unity C++原生插件必须用extern "C"导出函数以避免name mangling,且需严格匹配ABI、平台架构、C++标准及调用约定,内存管理须由C#主导。 Unity C++ 原生插件必须导出 C 风格函数 Unity 的原生插件机制只识别
extern "C"
导出的符号,C++ 编译器默认会做名字修饰(name mangling),导致
DllImport
找不到函数。不加
extern "C"
,哪怕函数签名完全匹配,也会报
DllNotFoundException
或
EntryPointNotFoundException
。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 所有供 C# 调用的函数必须包在
extern "C" { ... }
块里 避免重载函数——C 风格导出不支持重载,同名函数只会保留一个符号 头文件中用
#ifdef __cplusplus
包裹,确保 C# 项目引用时不会误解析 C++ 特性 Windows 下推荐用
__declspec(dllexport)
显式导出;macOS/Linux 用
__attribute__((visibility("default")))
,并编译时加
-fvisibility=hidden
防止符号污染 Unity 支持的平台 ABI 和二进制格式要严格匹配 Unity 不会帮你做 ABI 兼容转换。比如在 Windows 上用 MSVC 2019 编译的 x64 DLL,就不能被 IL2CPP + UWP 构建的 ARM64 Unity Player 加载;macOS 上用 Clang 15 编译的 dylib 若启用了
-std=c++20
,而 Unity Editor 自带的 libcpp 是 C++17 ABI,则可能在构造
std::string
时崩溃。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; Windows:统一用 Visual Studio 2019(或 Unity 官方文档标注的版本),目标平台选
x86_64
或
x86
,与 Unity Player 架构一致 macOS:用 Xcode 自带 Clang,C++ 标准限定为
-std=c++17
,禁用
-fblocks
等非标准扩展 Android:必须用 Unity 内置 NDK(如 r21e),且
APP_ABI := armeabi-v7a arm64-v8a
,生成的
.so
文件名要和
DllImport
中写的完全一致(含
lib
前缀和
.so
后缀) 构建后用
file
(macOS/Linux)或
DumpBin /headers
(Windows)检查目标架构是否匹配 C# DllImport 的路径、名称和调用约定不能写错 Unity 查找原生库依赖于平台规则,不是简单按文件名硬匹配。
DllImport("MyPlugin")
在 Windows 上实际找的是
MyPlugin.dll
,macOS 找
libMyPlugin.dylib
,Android 找
libMyPlugin.so
。路径错误、大小写不一致、漏掉
lib
前缀,都会直接触发
DllNotFoundException
。 C知道 CSDN推出的一款AI技术问答工具 下载 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 把插件放在
Assets/Plugins/
下,并按子目录区分平台:
Assets/Plugins/x86_64/
、
Assets/Plugins/Android/
、
Assets/Plugins/iOS/
C# 中
DllImport
的字符串只写“裸名”,不要带路径、扩展名或
lib
前缀(例如写
"MyPlugin"
,不是
"./Plugins/MyPlugin.dll"
) 显式指定
CallingConvention = CallingConvention.Cdecl
,避免 Windows 上因默认
StdCall
导致栈不平衡 Android 上需在
AndroidManifest.xml
或
gradle
中确保
android:extractNativeLibs="true"
(Unity 默认已设) 传参和内存管理必须由 C# 主导,C++ 插件不主动 new/delete 托管内存 C++ 插件里如果用
new
分配内存再传给 C#,或者试图
delete
C# 传来的
byte*
,大概率触发崩溃或 GC 异常。Unity 的托管堆和本地堆完全隔离,跨边界的内存必须明确所有权归属。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 基本类型(
int
、
float
、
bool
)直接传值,安全 字符串统一用
const char*
输入,C# 侧用
[MarshalAs(UnmanagedType.LPStr)]
;输出字符串则让 C++ 写入 C# 提前分配好的
StringBuilder
缓冲区 数组传参优先用
IntPtr
+ 长度参数,C++ 用
reinterpret_cast(ptr.ToPointer())
访问,避免封送开销 绝对不要在 C++ 里调用
Marshal.AllocHGlobal
或释放 C# 传来的指针——这块内存生命周期归 GC 管 C++ 插件最常崩在 ABI 不一致和字符串/数组封送方式错配上,尤其当 Unity 升级到新 LTS 版本后,自带的 C++ 运行时可能变化,老插件不重新编译就直接失效。别信“编译一次,到处运行”。

相关文章