Unity3D学习(九):C#和C++的相互调用
前言
不知不觉已经一年了,这一年来一直忙于公司项目疯狂加班,很少有自己的时间写下东西。不过好在项目最近也步入正轨了,正好抽空写点东西记录下学到的一些东西。
公司项目是一个端游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接口将信息打印了出来

参考资料
extern "c"用法解析 作者:JasonDing
Unity3D学习(九):C#和C++的相互调用的更多相关文章
- Unity3D 预备知识:C#与Lua相互调用
在使用Unity开发游戏以支持热更新的方案中,使用ULua是比较成熟的一种方案.那么,在使用ULua之前,我们必须先搞清楚,C#与Lua是怎样交互的了? 简单地说,c#调用lua, 是c# 通过Pin ...
- uLua学习笔记(三):Unity3D和Lua之间的相互调用
这篇笔记主要集中学习一下uLua和Unity3D之间相互调用的方法,我们导入了uLua之后,现在会弹出一个类似学习屏幕的东西,如下: 先赞一个! Unity3D调用Lua Unity3D调用Lua的方 ...
- Objective-C学习笔记(十九)——对象方法和类方法的相互调用
事实上在OC的对象方法(减号方法)和类方法(加号方法)并非相互独立的,它们也能够发生千丝万缕的关系,今天我们来研究下它们两者相互调用的问题.该样例还是以People类为基础. (一)对象方法调用类方法 ...
- Unity3D中C#和js方法相互调用
通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...
- Unity3d 脚本相互调用
unity中三种调用其他脚本函数的方法 第一种,被调用脚本函数为static类型,调用时直接用 脚本名.函数名().很不实用…… 第二种,GameObject.Find("脚本所在物体名& ...
- Unity3d 与IOS 相互调用
Unity3d 与IOS 相互调用 @灰太龙 群63438968 我用的Unity3d 4.2版本,这一节说一下IOS与U3D的交互! 首先在U3D中写个方法:这个时候导出为ios代码必须是真机,模拟 ...
- C++基础学习笔记----第四课(函数的重载、C和C++的相互调用)
本节主要讲了函数重载的主要概念以及使用方法,还有C和C++的相互调用的准则和具体的工程中的使用技巧. 函数重载 1.基本概念 函数重载就是用同一个函数名来定义不同的函数.使用不同的函数参数来搭配同一个 ...
- Android JNI学习(三)——Java与Native相互调用
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- unity3d学习笔记(一) 第一人称视角实现和倒计时实现
unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...
- Unity3D学习笔记2——绘制一个带纹理的面
目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...
随机推荐
- SPSS Clementine 数据挖掘入门2
下面使用Adventure Works数据库中的Target Mail作例子,通过建立分类树和神经网络模型,决策树用来预测哪些人会响应促销,神经网络用来预测年收入. Target Mail数据在SQL ...
- 高性能WEB开发:DOM编程
我们知道,DOM是用于操作XML和HTML文档的应用程序接口,用脚本进行DOM操作的代价很昂贵.有个贴切的比喻,把DOM和Javascript(这里指ECMscript)各自想象为一个岛屿,它们之间用 ...
- Android -- Service的开启关闭与生命周期
Service是Android 系统中的四大组件之一,是在一段不定的时间运行在后台,不和用户交互应用组件. service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity ...
- C# 解决无法识别的属性 configProtectionProvider
在使用.Net自身提供的加密本配置文件后再用System.Configuration.ConfigurationManager.AppSettings["key"]获取值时会出现“ ...
- ASCII对比表
ASCII控制字符和ASCII可显示字符 ASCII控制字符 二进制 十进制 十六进制 缩写 能够显示的表示法 名称/意义 0000 0000 0 00 NUL ␀ 空字符(Null) 0000 00 ...
- 【PAT】1028. List Sorting (25)
题目链接:http://pat.zju.edu.cn/contests/pat-a-practise/1028 题目描述: Excel can sort records according to an ...
- Android SDK镜像的介绍使用【转发】
由于一些原因,Google相关很多服务都无法访问,所以在很多时候我们SDK也无法升级,当然通过技术手段肯定可以解决,但是比较麻烦,而且下载速度也不怎么样. 这里笔者介绍一个国内的Android镜像站, ...
- 旧文备份:简单CANOpen 协议说明
(十年前的旧文,不舍等扔) 创建日期:2005-11-17 修改日期:2005-11-17 文件名称:简单CANOpen 协议说明.doc 作者:winshton 版本:V1.0 (注:本文以24in ...
- Android WiFi热点7.1以上版本适配
代码地址如下:http://www.demodashi.com/demo/13907.html 一.准备工作 开发环境: jdk1.8 AS(3.0.1) 运行环境: 华为V10(Android ...
- Android开发之Google Map
2013-07-03 Google Map 提供三种视图: 1. 传统的矢量地图,提供行政区域.交通以及商业信息等. 2. 不同分辨率的卫星照片,与Google Earth 基本一样. 3. 地形地图 ...