UE(虚幻)学习(三) UnrealSharp插件中调用非托管DLL

上一篇文章中我使用UnrealSharp成功使用了我的一个C#控制台程序中的网络模块,这个程序是基于KCP网络了,其中调用了Cmake 编译的一个C++的DLL,在虚幻中DLL需要放在Binaries目录中才可以。Unity中只要放在任意Plugins目录中就可以。

但是Binaries目录版本控制一般不提交,我们可以改一下,改成按照路径加载。

修改前的脚本

csharp 复制代码
using System.Runtime.InteropServices;

//脚本修改自//https://github.com/a11s/kcp_warpper

namespace NetLibrary
{
    
    public unsafe class KCP
    {


        const string LIBNAME = "libikcp.dll";
        //---------------------------------------------------------------------
        // interface
        //---------------------------------------------------------------------

        /// <summary>
        /// create a new kcp control object, 'conv' must equal in two endpoint
        /// from the same connection. 'user' will be passed to the output callback
        /// output callback can be setup like this: 'kcp->output = my_udp_output'
        /// </summary>
        /// <param name="conv"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        [DllImport(LIBNAME, EntryPoint = "ikcp_create", CallingConvention = CallingConvention.Cdecl)]
        public static extern IKCPCB* ikcp_create(uint conv, void* user);

        /// <summary>
        /// release kcp control object
        /// </summary>
        /// <param name="kcp"></param>
        [DllImport(LIBNAME, EntryPoint = "ikcp_release", CallingConvention = CallingConvention.Cdecl)]
        public static extern void ikcp_release(IKCPCB* kcp);

        /// <summary>
        /// set output callback, which will be invoked by kcp
        ///public static extern void ikcp_setoutput(IKCPCB* kcp, int (* output)(byte* buf, int len,             ikcpcb *kcp, void* user));
        /// </summary>
        /// <param name="kcp"></param>
        /// <param name="d_output"></param>
        [DllImport(LIBNAME, EntryPoint = "ikcp_setoutput", CallingConvention = CallingConvention.Cdecl)]
        public static extern void ikcp_setoutput(IKCPCB* kcp, System.IntPtr d_output);

       

篇幅太大没有必要,只展示部分代码片段。

可以看到之前是通过DllImport 载入LIBNAME变量来载入DLL的。

改为动态路径

先放上所有改动

csharp 复制代码
// 动态获取库路径
private static string GetLibraryPath()
{
    string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
    //动态库的路径被拼接到 Binaries\Managed\Third\libikcp.dll,而你的实际库文件存放在 E:\myproject\Third\libikcp.dll,这说明 AppDomain.CurrentDomain.BaseDirectory 返回的路径是 Binaries\Managed。
    baseDirectory = Path.Combine(baseDirectory, "../../");
    string relativePath;
    
    // 根据平台选择库路径
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        relativePath = "ThirdParty/Kcp/Win64/libikcp.dll";
    }
    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")))
    {
        relativePath = "ThirdParty/Kcp/Android/arm64-v8a/libikcp.so";
    }
    else
    {
        throw new PlatformNotSupportedException("Unsupported platform");
    }

    return Path.Combine(baseDirectory, relativePath);
}

// DllImport 使用动态路径
private const string LIBNAME = "PLACEHOLDER"; // 占位符
private static bool _isResolverSet = false;
// 添加初始化方法以动态设置路径
public static void Initialize()
{
    if (_isResolverSet)
    {
        Loger.Debug($"BSserver DLL: 已经加载过了 .");
        return;
    }
    try
    {
        string libraryPath = GetLibraryPath();
        
        //Loger.Error($"BSserver DLL:{libraryPath}");
        NativeLibrary.SetDllImportResolver(typeof(KCP).Assembly, (name, assembly, path) =>
        {
            if (name == LIBNAME)
            {
                return NativeLibrary.Load(libraryPath);
            }
            return IntPtr.Zero;
        });
        _isResolverSet = true;
    }
    catch(Exception e) {
        //AClientMain.inst.PrintString("C# : DLL:" + libraryPath);
        Console.WriteLine($"Loaded library 加载错误 ."+e.Message);
        Loger.Error($"BSserver DLL 加载错误 : " + e.Message);
    }
    
}
//const string LIBNAME = "libikcp.dll";
//---------------------------------------------------------------------
// interface
//---------------------------------------------------------------------

/// <summary>
/// create a new kcp control object, 'conv' must equal in two endpoint
/// from the same connection. 'user' will be passed to the output callback
/// output callback can be setup like this: 'kcp->output = my_udp_output'
/// </summary>
/// <param name="conv"></param>
/// <param name="user"></param>
/// <returns></returns>
[DllImport(LIBNAME, EntryPoint = "ikcp_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IKCPCB* ikcp_create(uint conv, void* user);

/// <summary>
/// release kcp control object
/// </summary>
/// <param name="kcp"></param>
[DllImport(LIBNAME, EntryPoint = "ikcp_release", CallingConvention = CallingConvention.Cdecl)]
public static extern void ikcp_release(IKCPCB* kcp);

使用方法:

我们在UE的工程下创建目录ThirdParty,按照代码里的路径,把DLL放进去,按照不同平台。

在加载DLL之前,我们需要调用Initialize方法初始化动态设置路径就可以了。

这些代码是ChatGPT帮忙写的,经过几次修改有了这段代码。

小技巧

以前是内事不决问baidu,外事不决问google,现在是内事问豆包,外事问ChatGPT。 :)

但是小心AI一本正经的胡说八道。 :P

相关推荐
EQ-雪梨蛋花汤15 小时前
【WebAR-图像跟踪】在Unity中基于Imagine WebAR实现AR图像识别
unity·游戏引擎·ar
子燕若水15 小时前
UE(虚幻引擎)运行项目时缺少插件/摸块,需要手动编译
游戏引擎·虚幻
向宇it17 小时前
【从零开始入门unity游戏开发之——C#篇32】C#其他不常用的泛型数据结构类、顺序存储和链式存储
java·开发语言·数据结构·unity·c#·游戏引擎
虾球xz21 小时前
游戏引擎学习第64天
redis·学习·游戏引擎
虾球xz1 天前
游戏引擎学习第63天
学习·游戏引擎
Unity_RAIN1 天前
Unity 战斗系统中角色UI血条设计
ui·unity·游戏引擎
ue星空1 天前
UE5仿漫威争锋灵蝶冲刺技能
ue5·虚幻·unreal engine
浅陌sss2 天前
Unity性能优化 --- 减少OverDraw
unity·性能优化·游戏引擎
向宇it2 天前
【从零开始入门unity游戏开发之——C#篇30】C#常用泛型数据结构类——list<T>列表、`List<T>` 和数组 (`T[]`) 的选择
java·开发语言·数据结构·unity·c#·游戏引擎·list