昨天学习了一下 Unity 调用 C++ 代码,实现了最基本的从编辑器中导出 DLL 并在Unity 的C# 中通过 DLLImport 来调用C++方法的功能,但总感觉这个流程过于繁琐,DeepSeek 后得知了 Swig 这个工具,能生成C#接口,方便调用C++逻辑。网上找的教程实践下来有点问题,好在目前解决了,我在这里记录一下整体的流程。
学习自
Unity/C++混合编程全攻略——Swig篇
C#利用SWIG调用C++的DLL
具体的 Swig 使用方法还需读者自行学习,本文仅用来记录环境安装和测试过程
本人的编辑器版本是 VS2022
环境准备阶段
Swig 下载地址
我选择的红框内的下载地址,因为里面包含可执行exe
配置环境变量中的Path
在命令行工具中输入有如下反馈则说明安装ok了
基础项目准备
新建工程 TestCallCpp
我们设置工程的属性:
C++代码编写
添加类 Misc,我们在其中放入工具方法,方便后续在 Unity 中调用
// 文件 Misc.h
#pragma once
extern "C"
{
float GetDistance(float x1, float y1, float x2, float y2);
}
// 文件 Misc.cpp
#include "Misc.h"
#include <cmath>
float GetDistance(float x1, float y1, float x2, float y2)
{
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
为什么通常推荐使用
extern "C"
C++的名称修饰(Name Mangling):C++编译器会对函数名进行修饰(根据函数名、参数类型、命名空间等生成唯一符号),这会导致你在Unity/C#中通过[DllImport]
查找函数时失败,因为函数名可能被修饰成类似?MyFunction@@YAXH@Z
的形式。
extern "C"
的作用:它会禁用C++的名称修饰,确保函数以C风格的简单名称(如MyFunction
)导出,方便其他语言(如C#)通过名称直接调用。
右键工程并生成之后,我们在这个路径下找到了生成的DLL(这个DLL不要用,这里是只验证项目是可运行的)
好的,我们工程准备完成了,下一步使用 swig 生成 C# 和对应的dll文件
生成流程
我们在工程下创建一个 swig 的筛选目录,并在其下创建一个 wrapper.i
的自定义文件
我们右键 wrapper.i
点击属性,修改项类型为“自定义生成工具”,并修改如下内容
- 修改
命令行
echo on
swig.exe -c++ -csharp -outdir "$(SolutionDir)\swig_wrap_result" "%(FullPath)"
echo off
命令行为执行 swig 并输出C#代码到项目的 swig_wrap_result 目录下,我们需要在工程根目录下创建 swig_wrap_result 目录
- 修改
输出
%(Filename)_wrap.cxx
输出一个服务于生成dll的中间件(个人理解)
- 勾选
将输出视为内容
为 True
接下来我们编辑 wrapper.i 文件
%module TestCallCpp
%{
#include "Misc.h"
%}
%include "Misc.h"
右键 wrapper.i 点击后,我们发现在工程里生成了一个 wrapper_wrap.cxx
的文件
此时我们在 swig 下添加现有项 wrapper_wrap.cxx
并确保其 配置属性->C/C++->预编译头
为“不使用预编译头”
最后,我们右键工程,并点击生成后,等待小段时间后,我们会发现生成了如下 C# 文件
和如下目录中的DLL文件
C#(Unity)使用
我们新建一个Unity工程,在 Assets 下创建 Plugins 目录,并将 DLL 丢进来,然后复制 上述步骤中生成的 C# 脚本到工程下
Unity 的工程目录如下
在 Unity 中,我们创建一个 Test 脚本并挂载在一个空 Object 上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
Vector2 one = Vector2.zero;
Vector2 two = new Vector2(10, 0);
float distance = TestCallCpp.GetDistance(one.x, one.y, two.x, two.y);
Debug.Log(distance);
}
}
点击执行后:
成功
编译导出脚本
TODO