前言

不知不觉已经一年了,这一年来一直忙于公司项目疯狂加班,很少有自己的时间写下东西。不过好在项目最近也步入正轨了,正好抽空写点东西记录下学到的一些东西。

公司项目是一个端游IP移植手游,端游是基于C++开发的,所以在开发手游的过程中还是复用了不少端游的核心逻辑代码,将其导出为DLL给Unity的C#调用。

这篇文章将会简单介绍下C#和C++之间如何提供接口给对方互相调用。

准备工作

1.新建一个C++空项目

右键项目,打开属性一栏,设置好输出目录以及生成目标类型。(注意x86和x64的生成目录有差异

添加名为DllInterface的.h头文件和.cpp文件

2. 新建一个Unity空项目

打开Unity创建一个空项目,添加一个Main.cs的MonoBehaviour脚本作为程序入口,再添加一个DllInterface.cs空类作为接口调用。

代码编写

1.C#调用C++

假设有这么一个需求:我想通过让C#调用C++的接口计算两个物体之间的平面距离(xy坐标系)。

首先,我们在C++项目DllInterface.h头文件中添加如下代码

#pragma once
#include<math.h>
#include<string.h>
#include<iostream>
#define _DllExport _declspec(dllexport) //使用宏定义缩写下 extern "C"
{
float _DllExport GetDistance(float x1, float y1, float x2, float y2);
}

其中 _declspec(dllexport)  用于将该函数标记为导出函数。extern "c" 是让该区域的代码作为C语言来编译,避免C++编译时因函数重载令函数名改变而导致C#调用的时候找不到该函数。

有关 extern 关键字的详解可参考这篇文章

在DllInterface.cpp文件添加GetDistance函数的实现。

float GetDistance(float x1, float y1, float x2, float y2)
{
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

然后在DllInterface.cs文件中添加如下代码

using System;
using System.Runtime.InteropServices;
using UnityEngine; public class DllInterface { [DllImport("CppInterface")]
public static extern float GetDistance(float x1, float y1, float x2, float y2);
}  

其中DllImport属性用于标记调用C++的Dll中与该C#函数同名的函数。

在Main.cs中添加如下代码

using UnityEngine;

public class Main : MonoBehaviour {

    private GameObject cube1;
private GameObject cube2;
// Use this for initialization
void Start () {
cube1 = GameObject.Find("Cube1");
cube2 = GameObject.Find("Cube2");
PrintDistanceViaUnity();
} void PrintDistanceViaUnity()
{
var pos1 = cube1.transform.position;
var pos2 = cube2.transform.position;
Debug.Log("This is a log from Unity");
Debug.Log("Distance:" + DllInterface.GetDistance(pos1.x, pos1.y, pos2.x, pos2.y));
}
}

新建一个空场景,新建两个立方体命名为Cube1和Cube2,再新建一个空物体命名为Main并将Main.cs脚本挂载在该物体上。

右键C++的解决方案,生成Dll到Unity对应的目录中。

注意:如果Unity已经在运行并且Dll已经存在,那么新的Dll写入生成会失败,此时需要关掉Unity再重新生成。

成功后点击运行按钮,可以看到输出如下,说明成功调用了C++的距离计算函数。

 2.C++调用C#

又比如这么一个需求:我想将C++的一些数据日志输出到Unity的控制台中方便查看信息和调试。

简单来看,就是将C#的函数引用传递给C++保存起来,然后C++通过函数指针调用C#。

修改DllInterface.h头文件的代码,如下

#pragma once
#include<math.h>
#include<string.h>
#include<iostream>
#define _DllExport _declspec(dllexport) #define UnityLog(acStr) char acLogStr[512] = { 0 }; sprintf_s(acLogStr, "%s",acStr); Debug::Log(acLogStr,strlen(acStr)); extern "C"
{
//C++ Call C#
class Debug
{
public:
static void (*Log)(char* message,int iSize);
}; // C# call C++
void _DllExport InitCSharpDelegate(void (*Log)(char* message, int iSize)); float _DllExport GetDistance(float x1, float y1, float x2, float y2);
}

修改DllInterface.cpp文件的代码,如下

#include "DllInterface.h"

void(*Debug::Log)(char* message, int iSize);

float GetDistance(float x1, float y1, float x2, float y2)
{
UnityLog("GetDistance has been called");
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
} void InitCSharpDelegate(void(*Log)(char* message, int iSize))
{
Debug::Log = Log;
UnityLog("Cpp Message:Log has initialized");
}

  

修改DllInterface.cs文件的代码,如下

using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine; public class DllInterface { [DllImport("CppInterface")]
public static extern float GetDistance(float x1, float y1, float x2, float y2); public delegate void LogDelegate(IntPtr message, int iSize); [DllImport("CppInterface")]
public static extern void InitCSharpDelegate(LogDelegate log); //C# Function for C++'s call
[MonoPInvokeCallback(typeof(LogDelegate))]
public static void LogMessageFromCpp(IntPtr message, int iSize)
{
Debug.Log(Marshal.PtrToStringAnsi(message, iSize));
}
}

最后修改Main.cs的代码

using UnityEngine;

public class Main : MonoBehaviour {

    private GameObject cube1;
private GameObject cube2;
// Use this for initialization
void Start () {
cube1 = GameObject.Find("Cube1");
cube2 = GameObject.Find("Cube2");
//pass C#'s delegate to C++
DllInterface.InitCSharpDelegate(DllInterface.LogMessageFromCpp);
PrintDistanceViaUnity();
} void PrintDistanceViaUnity()
{
var pos1 = cube1.transform.position;
var pos2 = cube2.transform.position;
Debug.Log("This is a log from Unity");
Debug.Log("Distance:" + DllInterface.GetDistance(pos1.x, pos1.y, pos2.x, pos2.y));
}
}

关掉unity重新生成C++的Dll,成功后再打开Unity项目运行场景,可以看到如下打印,说明C++成功调用了Unity的Log接口将信息打印了出来

Exampe项目下载

参考资料

Unity/C++混合编程全攻略!——基础准备      作者:董宸

官方Manual

extern "c"用法解析     作者:JasonDing

Unity3D学习(九):C#和C++的相互调用的更多相关文章

  1. Unity3D 预备知识:C#与Lua相互调用

    在使用Unity开发游戏以支持热更新的方案中,使用ULua是比较成熟的一种方案.那么,在使用ULua之前,我们必须先搞清楚,C#与Lua是怎样交互的了? 简单地说,c#调用lua, 是c# 通过Pin ...

  2. uLua学习笔记(三):Unity3D和Lua之间的相互调用

    这篇笔记主要集中学习一下uLua和Unity3D之间相互调用的方法,我们导入了uLua之后,现在会弹出一个类似学习屏幕的东西,如下: 先赞一个! Unity3D调用Lua Unity3D调用Lua的方 ...

  3. Objective-C学习笔记(十九)——对象方法和类方法的相互调用

    事实上在OC的对象方法(减号方法)和类方法(加号方法)并非相互独立的,它们也能够发生千丝万缕的关系,今天我们来研究下它们两者相互调用的问题.该样例还是以People类为基础. (一)对象方法调用类方法 ...

  4. Unity3D中C#和js方法相互调用

    通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...

  5. Unity3d 脚本相互调用

    unity中三种调用其他脚本函数的方法 第一种,被调用脚本函数为static类型,调用时直接用  脚本名.函数名().很不实用…… 第二种,GameObject.Find("脚本所在物体名& ...

  6. Unity3d 与IOS 相互调用

    Unity3d 与IOS 相互调用 @灰太龙 群63438968 我用的Unity3d 4.2版本,这一节说一下IOS与U3D的交互! 首先在U3D中写个方法:这个时候导出为ios代码必须是真机,模拟 ...

  7. C++基础学习笔记----第四课(函数的重载、C和C++的相互调用)

    本节主要讲了函数重载的主要概念以及使用方法,还有C和C++的相互调用的准则和具体的工程中的使用技巧. 函数重载 1.基本概念 函数重载就是用同一个函数名来定义不同的函数.使用不同的函数参数来搭配同一个 ...

  8. Android JNI学习(三)——Java与Native相互调用

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  9. unity3d学习笔记(一) 第一人称视角实现和倒计时实现

    unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...

  10. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

随机推荐

  1. [置顶] JDK工具(一)–Java编译器javac

    1.概述    javac.exe: Java编译器,将Java源代码转换成字节码. 2.用法    javac <选项> <源文件> (使用过程中发现,javac <源 ...

  2. WebService_java编写Webservice_Axis2_1.6

    最近给某省国家电网写一套系统,由于内部数据库单向隔离装置不支持ODBC, 原来c#写的webservice 和.net ,iis就需要换成java这一套... 下面是用Axis2 写的webservi ...

  3. hadoop实战--搭建开发环境及编写Hello World

    本文地址:http://www.cnblogs.com/archimedes/p/hadoop-helloworld.html,转载请注明源地址. 欢迎关注我的个人博客:www.wuyudong.co ...

  4. 设置 IE 默认模式为 IE8

    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />

  5. Transfrom在64bit服务下面无法运行

    利用TR发布了一个cube到Cognos connection里面,利用Analysis Studio打开的时候报错如下 问题是很简单的,按照报错内容看是当前的64bit的report  servic ...

  6. 介绍Visual Studio的Android模拟器

    介绍Visual Studio的Android模拟器 http://blogs.msdn.com/b/visualstudioalm/archive/2014/11/12/introducing-vi ...

  7. 【Android】Activity 生命周期具体解释

    与其它编程模式不同,android中的Activity没有main()函数.我们无法决定Activity的创建和销毁过程,Activiy的创建和销毁(即生命周期)由系统完毕,系统会在Activity的 ...

  8. SQL语句中拆分字段

    SELECT PARSENAME(replace(MONITOR_ROOM_ID,'^' , '.'), 1) AS RoomID FROM ZY_MONITOR_ROOM 遇到以前系统高人设计的表, ...

  9. FileAlreadyExistsException: Output directory hdfs://ubuntu:9000/output09 already exists

    14/07/21 17:49:59 ERROR security.UserGroupInformation: PriviledgedActionException as:chenlongquan ca ...

  10. http协议中content-length 以及chunked编码分析

    转载请注明出处 http://blog.csdn.net/yankai0219/article/details/8269922 0.序 1.http/1.1协议中与chunked编码的相关字段 1)E ...