Unity3D學習(九):C#和C++的相互調用

前言html

不知不覺已經一年了,這一年來一直忙於公司項目瘋狂加班,不多有本身的時間寫下東西。不過好在項目最近也步入正軌了,正好抽空寫點東西記錄下學到的一些東西。ios

公司項目是一個端遊IP移植手遊,端遊是基於C++開發的,因此在開發手遊的過程當中仍是複用了很多端遊的核心邏輯代碼,將其導出爲DLL給Unity的C#調用。git

這篇文章將會簡單介紹下C#和C++之間如何提供接口給對方互相調用。github

準備工做編程

1.新建一個C++空項目函數

右鍵項目,打開屬性一欄,設置好輸出目錄以及生成目標類型。(注意x86和x64的生成目錄有差別this

添加名爲DllInterface的.h頭文件和.cpp文件spa

2. 新建一個Unity空項目3d

打開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

相關文章
相關標籤/搜索