Unity显示C++动态库中的Log
引子
需求是Unity的项目调用C++动态库的逻辑,并进行调试,这时就需要能够通过Log,观测C++动态库中的逻辑是否正常运行。
这个需求的底层逻辑其实就是,在C++中做一个“信息发送器”,C#中有一个“接收器”。在Log信息时,就是C++将Log内容,发送给C#,从而在Unity中显示出来。
该文章中的代码经过实际项目检测,可以直接使用。
C++部分
直接上代码:
头文件UnityDebug.hpp
#ifndef UnityDebug_hpp
#define UnityDebug_hpp
#include <stdio.h>
#include "string.h"
#define UnityLog(acStr, ...) Debug::L(acStr, ##__VA_ARGS__);
//C++ Call C#
class Debug
{
public:
static void (*Log)(char* message, int iSize);
static void L(const char* msg, ...);
};
extern "C" void InitCSharpDelegate(void (*Log)(char* message, int iSize));
#endif /* UnityDebug_hpp */
源文件UnityDebug.cpp
#include <stdarg.h>
#include "UnityDebug.hpp"
void (*Debug::Log)(char* message, int iSize);
void Debug::L(const char* fmt, ...)
{
if(Debug::Log == NULL)return;
char acLogStr[512];// = { 0 };
va_list ap;
va_start(ap, fmt);
vsprintf(acLogStr, fmt, ap);
va_end(ap);
Debug::Log(acLogStr, strlen(acLogStr));
}
void InitCSharpDelegate(void(*Log)(char* message, int iSize))
{
Debug::Log = Log;
UnityLog("Cpp Message:Log has initialized");
}
C++中的使用
创建完以上两个文件后,在需要Log的地方,调用UnityLog("*****");
即可。具体用法与printd()
函数一样。
Unity的C#代码
using AOT;
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class DllLog
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogDelegate(IntPtr message, int iSize);
[DllImport("macVTA", CallingConvention = CallingConvention.Cdecl)]
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));
}
public static void ShowLog()
{
InitCSharpDelegate(LogMessageFromCpp);
}
}
Unity端的使用
在程序运行的一开始调用一次ShowLog();
即可,这样C++中的所有Log都会在相应的程序位置显示在Unity控制台中。
逻辑原理
将C++与C#的代码都写好,并测试成功后,再来对照着看一下其中的逻辑原理。
- 首先,在C++中调用的
UnityLog("*****");
,通过头文件(UnityDebug.hpp)可以知道,它其实就是类Debug
的L
函数(Debug::L
); - 该函数方法的实现,就是将参数进行了一些转换/合并,最后传入了函数指针
Debug::Log
; - 而函数指针
Debug::Log
,则是在经过关键字extern "C"
声明后的外部函数方法void InitCSharpDelegate(void (*Log)(char* message, int iSize));
的实现中进行了“赋值”; InitCSharpDelegate
作为外部方法,对应了C#代码中被声明的public static extern void InitCSharpDelegate(LogDelegate log);
,其中的参数LogDelegate
,则是对应了C++代码中的static void (*Log)(char* message, int iSize);
函数指针的public delegate void LogDelegate(IntPtr message, int iSize);
委托;- Unity的C#代码调用的
ShowLog();
,就是将函数LogMessageFromCpp
作为委托(或函数指针)传入了外部方法(extern
关键字)InitCSharpDelegate
中; - 这样在C++程序调用
UnityLog
方法时,其实就是将信息进行转化/合并后,传入了委托/函数指针LogMessageFromCpp
中,从而显示在了Unity控制台上。