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

相关推荐
Clank的游戏栈1 小时前
Unity基于GraphView的可视化关卡编辑器开发指南
unity·编辑器·游戏引擎
三巧14 小时前
Godot 敌人生成半径和围墙不匹配,导致敌人错误的生成在围墙外的解决代码
游戏引擎·godot
技术小甜甜14 小时前
【Godot引擎】如何使用内置的全局搜索功能提升开发效率
游戏引擎·godot
技术小甜甜14 小时前
【Godot】如何导出 Release 版本的安卓项目
android·游戏引擎·godot
ChiLi_Lin1 天前
Unity异常上报飞书工具
unity·游戏引擎·飞书
Magnum Lehar1 天前
vulkan游戏引擎的makefile启动环境实现
游戏引擎
地狱为王1 天前
基于VLC的Unity视频播放器(四)
unity·游戏引擎·音视频
1 天前
Unity与Excel表格交互热更方案
unity·游戏引擎·excel
装大炮的自行车2 天前
【Unity】R3 CSharp 响应式编程 - 使用篇(集合)(三)
windows·unity·游戏引擎
阿松爱学习2 天前
【Unity开发】控制手机移动端的震动
unity·游戏引擎·unity开发