1. The way of DLL:
C++ code: compiled into DLL, imported into Unity
#pragma once
#include <map>
#include <string>
//导出宏定义
#define _DllExport _declspec(dllexport)
//函数指针
typedef void (*NativeCallback)(const char*);
extern "C"
{
//注意这里字符串不能用C++的std::string,和C#的string不等价,等价的是char*,即字符数组
_DllExport void RegisterNativeCallback(const char* functionName, NativeCallback callback);
_DllExport void UpdateNative();
}
//缓存C#函数的地址
std::map<std::string, NativeCallback> _callbackMap;
//导出让C#调用,注册C#端的函数,注册后就可以在C++端调用C#的函数,本质就职把C#函数的地址给到C++,C++调用。
_DllExport void RegisterNativeCallback(const char* functionName, NativeCallback callback)
{
_callbackMap[std::string(functionName)] = callback;
}
//导出让C#调用
_DllExport void UpdateNative()
{
//调用C#端的函数,可以在Unity性能分析界面看到Native update,用此方法可以在Unity界面查看C++代码的耗时
_callbackMap[std::string("BeginSample")]("Native update");
_callbackMap[std::string("Log")]("Native Log");
_callbackMap[std::string("EndSample")]("Native update");
}
C# code:
using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Profiling;
namespace Haha
{
public class TestCPPLib : MonoBehaviour
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void NativeCallback(string args);
[DllImport("CPPLib")]
static extern void RegisterNativeCallback(string functionName, IntPtr callback);
[DllImport("CPPLib")]
static extern void UpdateNative();
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackLog(string args)
{
Debug.Log(args);
}
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackBeginSample(string args)
{
Profiler.BeginSample(args);
}
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackEndSample(string args)
{
Profiler.EndSample();
}
void Start()
{
//调用C++函数,注册C#函数
RegisterNativeCallback("Log", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackLog)));
RegisterNativeCallback("BeginSample", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackBeginSample)));
RegisterNativeCallback("EndSample", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackEndSample)));
}
private void Update()
{
//调用C++函数
UpdateNative();
}
}
}
2. There is also a C++ source code as a plug-in, which only supports il2cpp.
C++ code: put it directly in the Assets directory of Unity
C# source file: The difference is that you don’t write the specific file name when importing, just write: __Internal, because using the IL2CPP backend method will put the C++ source file inside the project and compile it together.
using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Profiling;
namespace Haha
{
public class TestCPPLib : MonoBehaviour
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void NativeCallback(string args);
//区别在这里:
[DllImport("__Internal")]
static extern void RegisterNativeCallback(string functionName, IntPtr callback);
[DllImport("__Internal")]
static extern void UpdateNative();
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackLog(string args)
{
Debug.Log(args);
}
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackBeginSample(string args)
{
Profiler.BeginSample(args);
}
[MonoPInvokeCallback(typeof(NativeCallback))]
static void CallbackEndSample(string args)
{
Profiler.EndSample();
}
void Start()
{
RegisterNativeCallback("Log", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackLog)));
RegisterNativeCallback("BeginSample", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackBeginSample)));
RegisterNativeCallback("EndSample", Marshal.GetFunctionPointerForDelegate(new NativeCallback(CallbackEndSample)));
}
private void Update()
{
UpdateNative();
}
}
}
If you just look at the time-consuming C++ code, unity provides a C++ interface, which is more convenient, and the video is introduced at the end.
Unity official manual
//使用这两个原生接口,需要在C++项目属性配置:VC++目录->包含目录 或 C/C++->常规->附加包含目录,添加Unity这两个文件的文件路径
#include <IUnityInterface.h>
#include <IUnityProfiler.h>
static IUnityProfiler* s_UnityProfiler = NULL;
static const UnityProfilerMarkerDesc* s_MyPluginMarker = NULL;
static bool s_IsDevelopmentBuild = false;
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces * unityInterfaces)
{
s_UnityProfiler = unityInterfaces->Get<IUnityProfiler>();
if (s_UnityProfiler == NULL)
return;
s_IsDevelopmentBuild = s_UnityProfiler->IsAvailable() != 0;
s_UnityProfiler->CreateMarker(&s_MyPluginMarker, "MyCustomMethod", kUnityProfilerCategoryOther, kUnityProfilerMarkerFlagDefault, 0);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload()
{
s_UnityProfiler = NULL;
}
extern "C" void UNITY_INTERFACE_EXPORT InterfaceUpdate()
{
if (s_IsDevelopmentBuild)
{
s_UnityProfiler->BeginSample(s_MyPluginMarker);
}
//测试耗时代码
for (int i = 0; i < 100000; i++)
{
}
if (s_IsDevelopmentBuild)
{
s_UnityProfiler->EndSample(s_MyPluginMarker);
}
}
About the test of IL2CPP packaging:
1. Build directly through Unity, the structure is as follows: as follows: size 313M
2. By creating a VS solution (you can set breakpoints in VS, modify and debug C++ code), build in VS, and build in the interface Settings:
The structure is as follows: the bin directory contains the data directory of the software, and the specific generated exe is under their respective platforms:
Finally, put the data folder of the bin directory and the exe of the corresponding platform together, as follows:
lib, pdb, and exp above the size of 162M can be deleted, and the test does not affect the operation. The simplified directory: size of 50.8M
Conclusion: the volume built with vs smaller.