新建一个空的UE插件项目
在插件根目录下的Source新建一个rust lib项目
html2markdown-dylib 项目名
cargo new --lib html2markdown-dylib

路径如下

配置Cargo.toml
html2markdown_dylib 记得改这,不要使用-横杠
[package]
name = "html2markdown-dylib"
version = "0.1.0"
edition = "2021"
[dependencies]
mdka = "1.2.10"
reqwest = { version = "0.12.12", features = ["blocking"] }
[lib]
crate-type = ["dylib"]
name = "html2markdown_dylib"
[build-dependencies]
cbindgen = "0.27.0"
编写实际代码
md_loader.rs
pub struct MdLoader;
impl MdLoader {
/// 从网络URL中加载markdown文本
pub fn load_md_from_url(url: &str) -> String {
let response = reqwest::blocking::get(url);
if let Ok(response) = response {
if let Ok(text) = response.text() {
let md = mdka::from_html(&text);
return md;
}
}
return format!("Loading markdown from url: {}", url);
}
}
构建头文件
在根目录下新建build.rs
extern crate cbindgen;
use std::env;
fn main() {
let create_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let mut config: cbindgen::Config = Default::default();
config.language = cbindgen::Language::Cxx;
cbindgen::generate_with_config(&create_dir, config)
.expect("unable to generate bindings")
.write_to_file("include/UEHTML2MARKDOWN.h");
}
编译
cargo build --release
会在include目录下生成一个头文件,后面会在ue项目中使用 像这样
#include "UEHTML2MARKDOWN.h"
最终结果

在UE插件中添加依赖
在UE插件名.Build.cs编辑
using System;
using System.IO;
using UnrealBuildTool;
public class UEHtml2Md : ModuleRules
{
public UEHtml2Md(ReadOnlyTargetRules Target) : base(Target)
{
if (Target.Platform == UnrealTargetPlatform.Mac)
{
Console.WriteLine($"梁典典:module directory path: {ModuleDirectory}");
var includePath = Path.Combine(ModuleDirectory, "../html2markdown-dylib/include");
Console.WriteLine($"梁典典:header include path{includePath}");
PublicIncludePaths.Add(includePath);
PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "../html2markdown-dylib","target", "release", "libhtml2markdown_dylib.dylib"));
RuntimeDependencies.Add("${PluginDir}/Binaries/Mac/libhtml2markdown_dylib.dylib");
}
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Projects",
"UEHtml2Md"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
在插件模块接口中加载句柄
文件: 插件名.h
#pragma once
#include "Modules/ModuleManager.h"
class FUEHtml2MdModule : public IModuleInterface
{
public:
void* Instance; // 句柄
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
文件:插件名.cpp
#include "UEHtml2Md.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "FUEHtml2MdModule"
void FUEHtml2MdModule::StartupModule()
{
const auto PluginDir = IPluginManager::Get().FindPlugin("UEHtml2Md")->GetBaseDir();
if (const auto LibPath = FPaths::Combine(*PluginDir, TEXT("Binaries/Mac/libhtml2markdown_dylib.dylib")); !LibPath.IsEmpty())
{
Instance = FPlatformProcess::GetDllHandle(*LibPath);
}
if (!Instance){
UE_LOG(LogTemp,Error,TEXT("ldd: load dll failed!"));
}else
{
UE_LOG(LogTemp,Log,TEXT("ldd: load dll success!"));
}
}
void FUEHtml2MdModule::ShutdownModule()
{
if (Instance)
{
FPlatformProcess::FreeDllHandle(Instance);
Instance = nullptr;
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FUEHtml2MdModule, UEHtml2Md)
最终: 在虚幻项目中引入插件
文件: 项目.Build.cs

使用
void AMyOutletBase::BeginPlay()
{
Super::BeginPlay();
// 测试加载,这里调用rust写的函数,可以在插件中封装更友好的类调用
const auto Result = load_markdown_form_url_ffi("https://www.rust-lang.org");
const FString Str = FString(Result);
UE_LOG(LogTemp, Display, TEXT("%s"), *Str);
if (TextComponent)
{
TextComponent->SetText(FText::FromString(Str));
}
}