前言
前段時(shí)間,研究了一套 Rust 接入 Maya Plugin 的玩法,主要原理還是使用 C ABI 去交互。那我想著 UE 是使用 C++ 寫的,肯定也可以使用 C ABI 去交互,如果可以的話在 UE 中就可以使用 Rust 代碼去跑,甚至還可以使用 Rust Crates,免得使用 C++ 去寫關(guān)于數(shù)據(jù)庫(kù)操作、加密操作等容易引發(fā)安全漏洞的代碼。所以我在昨天開(kāi)始了這個(gè)計(jì)劃,使用了 Rust 的 html2md 的庫(kù)在 UE 中使用,效果圖如下。
開(kāi)工
這個(gè)案例就是在 UE 中實(shí)現(xiàn) html2md,雖然實(shí)際效果可能沒(méi)卵用,主要目的還是帶大家跑下這套流程。
我們要實(shí)現(xiàn)的功能就是在 Level 放置一個(gè) Text Render。
游戲開(kāi)始階段,這個(gè) Text Render 就會(huì)拉取 Rust 官網(wǎng)頁(yè)面,并將它轉(zhuǎn)為 Markdown 格式展示在游戲中。
創(chuàng)建 UE 項(xiàng)目
我這里使用的版本是 5.0.1,大家使用 4.x 也是可以的。
我們創(chuàng)建一個(gè)第三人稱游戲 C++項(xiàng)目,命名為Html2mdExample。
創(chuàng)建 UE 插件
我們將 Html2md 的功能封裝成一個(gè)插件,這樣就可以在各個(gè)項(xiàng)目中去使用它。
我們創(chuàng)建一個(gè)空白插件,插件名隨意,我這邊就叫 html2md。
在插件中添加 Text Render
我們要在插件中添加一個(gè) Actor,作為處理 HTTP 請(qǐng)求,并渲染 Markdown 的 Text Render。
一定要選擇添加到插件中,而不是項(xiàng)目中。
TextRender.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Engine/Classes/Components/TextRenderComponent.h"
#include "TextRender.generated.h"
UCLASS()
class HTML2MD_API ATextRender : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
UTextRenderComponent* Text;
public:
ATextRender();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
};
TextRender.cpp
簡(jiǎn)單寫一寫代碼,添加一個(gè) UTextRenderComponent,并修改它的顏色、旋轉(zhuǎn)、縮放等屬性。
#include "TextRender.h"
ATextRender::ATextRender()
{
PrimaryActorTick.bCanEverTick = true;
Text = CreateDefaultSubobject<UTextRenderComponent>(TEXT("Text"));
Text->SetupAttachment(RootComponent);
}
void ATextRender::BeginPlay()
{
Super::BeginPlay();
Text->SetRelativeRotation(FRotator(90.f, 180.f, 0.f));
Text->SetTextRenderColor(FColor(0, 255, 225));
Text->SetRelativeScale3D(FVector(2.f, 2.f, 2.f));
}
void ATextRender::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
創(chuàng)建 Rust 項(xiàng)目
我們 Rust 項(xiàng)目要?jiǎng)?chuàng)建在 UE 插件項(xiàng)目目錄下。找到插件源碼目錄,與 C++ 源碼同級(jí)運(yùn)行以下命令創(chuàng)建項(xiàng)目。
cargo new --lib html2md-dylib
Cargo.toml
[package]
name = "html2md-dylib"
version = "0.1.0"
edition = "2021"
# 將庫(kù)打包成動(dòng)態(tài)鏈接庫(kù)
[lib]
crate-type = ["dylib"]
name = "html2md_dylib"
[dependencies]
# 用于 HTML 轉(zhuǎn)為 Markdown
html2md = "0.2.14"
# 用于進(jìn)行 HTTP 請(qǐng)求
reqwest = { version = "0.11.13", features = ["blocking"] }
[build-dependencies]
# 用于生成 C 頭文件
cbindgen = "0.24.3"
在這里我們實(shí)現(xiàn)一個(gè)從 HTTP 請(qǐng)求拉取 HTML 并轉(zhuǎn)為 Markdown 的實(shí)現(xiàn)。
pub struct MDLoader;
impl MDLoader {
pub fn load_md_from_url(url: &str) -> String {
let body = if let Ok(res) = reqwest::get(url) {
if let Ok(text) = res.text() {
text
} else {
return format!("Failed get {} text", url);
}
} else {
return format!("Failed get {} body", url);
};
html2md::parse_html(&body)
}
}
src/lib.rs
將函數(shù)導(dǎo)出,這樣在動(dòng)態(tài)鏈接庫(kù)中就可以調(diào)用這個(gè)函數(shù)了。
use std::{c_char, CStr, CString};
mod md_loader;
#[no_mangle]
pub extern "C" fn load_md_from_url_ffi(url: *const c_char) -> *const c_char {
let url = unsafe { CStr::from_ptr(url) };
let res = md_loader::load_md_from_url(&url.to_string_lossy());
CString::new(res).unwrap().into_raw()
}
build.rs
我們需要使用到構(gòu)建腳本來(lái)幫我們生成 C 頭文件,我們將在 C++ 代碼中使用它。
頭文件生成到 include/UEHtml2md.h
extern crate cbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let mut config: cbindgen::Config = Default::default();
config.language = cbindgen::Cxx;
cbindgen::generate_with_config(&crate_dir, config)
.expect("Unable to generate bindings")
.write_to_file("include/UEHtml2md.h");
}
html2md-dylib.build.cs
我們要添加一個(gè) Rust 項(xiàng)目名.build.cs,讓 UE 認(rèn)到我們的動(dòng)態(tài)鏈接庫(kù)。相關(guān)文檔
using System;
using System.IO;
using UnrealBuildTool;
public class Html2mdDyLib : ModuleRules
{
public Html2mdDyLib(ReadOnlyTargetRules Target) : base(Target)
{
Type = ModuleType.External;
if (Target.Platform == UnrealTargetPlatform.Win64)
{
// 添加頭文件目錄
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "include"));
// 添加 .lib
PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "target", "release", "html2md_dylib.dll.lib"));
// 添加 .dll
PublicDelayLoadDLLs.Add("html2md_dylib.dll");
// 我們需要將 .dll 文件復(fù)制到這邊
RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/html2md_dylib.dll");
}
}
}
構(gòu)建 Rust 項(xiàng)目
我們先運(yùn)行構(gòu)建命令
cargo build --release
然后將 html2md_dylib.dll 復(fù)制一份到 插件目錄/Binaries/Win64/html2md_dylib.dll。
這一步可以使用腳本去完成,我這邊就不寫了。
連接 Rust & UE
因?yàn)槲覀?Rust 項(xiàng)目目錄名不符合 UE 的規(guī)范,所以我們要將 html2md-dylib 目錄更改為 Html2mdDyLib,html2md-dylib.build.cs 也需要更為 Html2mdDyLib.build.cs。
將動(dòng)態(tài)鏈接庫(kù)添加到依賴
我們編輯 html2md.build.cs,也就是插件的構(gòu)建腳本。在 PublicDependencyModuleNames 添加 Html2mdDyLib 和 Projects。
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Html2mdDyLib",
"Projects",
// ... add other public dependencies that you statically link with here ...
}
);
插件加載動(dòng)態(tài)鏈接庫(kù)
html2md.h
插件頭文件中聲明 DLL 句柄
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class Fhtml2mdModule : public IModuleInterface
{
void* Html2mdLibraryHandle;
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
html2md.cpp
插件加載動(dòng)態(tài)鏈接庫(kù)
如果與本案例命名不同,記得替換代碼中的路徑
// Copyright Epic Games, Inc. All Rights Reserved.
#include "html2md.h"
#include "Core.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "Fhtml2mdModule"
void Fhtml2mdModule::StartupModule()
{
FString BaseDir = IPluginManager::Get().FindPlugin("html2md")->GetBaseDir();
FString Html2mdLibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/Win64/html2md_dylib.dll"));
Html2mdLibraryHandle = !Html2mdLibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*Html2mdLibraryPath) : nullptr;
if (Html2mdLibraryHandle == nullptr)
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ThirdPartyLibraryError", "Failed to load Html2mdLibrary"));
}
}
void Fhtml2mdModule::ShutdownModule()
{
FPlatformProcess::FreeDllHandle(Html2mdLibraryHandle);
Html2mdLibraryHandle = nullptr;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(Fhtml2mdModule, html2md)
Text Render 調(diào)用 Rust
終于來(lái)到了最后要實(shí)現(xiàn)的目標(biāo),我們將調(diào)用 Rust 接口,將返回值顯示在 Text Render 中。
TextRender.cpp
#include "TextRender.h"
#include "Html2mdDyLib/include/UEHtml2md.h"
ATextRender::ATextRender()
{
PrimaryActorTick.bCanEverTick = true;
Text = CreateDefaultSubobject<UTextRenderComponent>(TEXT("Text"));
Text->SetupAttachment(RootComponent);
}
void ATextRender::BeginPlay()
{
Super::BeginPlay();
// 在這里調(diào)用 Rust 接口
const char* text = "https://www.rust-lang.org/";
FString result = FString(load_md_from_url_ffi(text));
Text->SetText(FText::FromString(result)); // 設(shè)置 Text 內(nèi)容
Text->SetRelativeRotation(FRotator(90.f, 180.f, 0.f));
Text->SetTextRenderColor(FColor(0, 255, 225));
Text->SetRelativeScale3D(FVector(2.f, 2.f, 2.f));
}
void ATextRender::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
編譯項(xiàng)目
在 Visual Studio 或 虛幻引擎 中編譯都可以。
在 UE 中查看效果
我們將 TextRender 拖入場(chǎng)景。
運(yùn)行游戲!我們會(huì)發(fā)現(xiàn) Text Render 展示了 Rust 官網(wǎng)的內(nèi)容。
總結(jié)
通過(guò)這次案例,我發(fā)現(xiàn) Rust 可以在 UE 中做很多事情,我只是使用了 html2md 庫(kù)作為案例來(lái)演示,大家感興趣的話也可以去使用 ws,mysql 等,關(guān)于網(wǎng)絡(luò)通訊、數(shù)據(jù)庫(kù)、甚至可以在 Rust 中實(shí)現(xiàn)游戲功能的算法、狀態(tài)機(jī)等接入到虛幻引擎中使用。
能用少量并安全的代碼去編寫這些復(fù)雜的功能,何樂(lè)而不為呢?
用洛佳大佬的話來(lái)說(shuō):“如果996了一整天,每個(gè)開(kāi)發(fā)者都無(wú)法避免疲憊的自己忘記釋放指針或者釋放了兩次,很有可能一個(gè)漏洞就埋下來(lái)了。
能用編程語(yǔ)言理論檢查出來(lái)漏洞還是好事情。這也不意味著我可以做一個(gè)強(qiáng)行檢查 C++ 的編譯器來(lái)達(dá)到一樣的效果,因?yàn)檫@種理論要求整個(gè)語(yǔ)言要重新設(shè)計(jì),Rust 就是重新設(shè)計(jì)的結(jié)果”
審核編輯 :李倩
-
數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
7文章
3904瀏覽量
65816 -
編程語(yǔ)言
+關(guān)注
關(guān)注
10文章
1955瀏覽量
36183 -
Rust
+關(guān)注
關(guān)注
1文章
233瀏覽量
6975
原文標(biāo)題:Rust 在虛幻引擎 5 中的使用
文章出處:【微信號(hào):Rust語(yǔ)言中文社區(qū),微信公眾號(hào):Rust語(yǔ)言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
基于SEGGER的Ozone調(diào)試器和J-Trace工具跟蹤Ferrocene的Rust應(yīng)用
vivo打造最具影響力Rust賽事,點(diǎn)亮基礎(chǔ)軟件事業(yè)的“藍(lán)河時(shí)代”

二維影像掃描引擎可以應(yīng)用于哪些行業(yè)?

JavaScript與Rust和WebAssembly集成

容器引擎是什么意思
使用 AMD Versal AI 引擎釋放 DSP 計(jì)算的潛力
火山引擎推出豆包·視頻生成模型
如何用Rust編寫一個(gè)ChatGPT桌面應(yīng)用(保姆級(jí)教程)

微軟計(jì)劃在搜索引擎Bing中引入AI摘要功能
未來(lái)嵌入式系統(tǒng)的黃金搭檔 MCX N947遇上Rust

Vector和HighTec推出基于Rust和AUTOSAR Classic實(shí)現(xiàn)安全應(yīng)用的解決方案

使用PSoC5LP的過(guò)程中,遇到PSoC5LP在EFT干擾時(shí)復(fù)位的問(wèn)題怎么解決?
深入理解渲染引擎:打造逼真圖像的關(guān)鍵

評(píng)論