zoukankan      html  css  js  c++  java
  • 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接口将信息打印了出来

    Exampe项目下载

    参考资料

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

    官方Manual 

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

  • 相关阅读:
    单词统计
    第十周学习进度
    个人课程总结
    构建之法阅读笔记03
    构建之法阅读笔记02
    构建之法阅读笔记01
    第十六周学习报告
    计算最长英语单词链
    第十五周学习报告
    用户体验评价
  • 原文地址:https://www.cnblogs.com/0kk470/p/10607544.html
Copyright © 2011-2022 走看看