日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

uasset python_Unreal Python 结合 C++ 开发蓝图库插件

發(fā)布時(shí)間:2024/7/5 87 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uasset python_Unreal Python 结合 C++ 开发蓝图库插件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文章轉(zhuǎn)載自 智傷帝的個(gè)人博客 - 原文鏈接

前言

上個(gè)月的這個(gè)時(shí)候我寫了一篇文章關(guān)于如何嵌入 PySide 調(diào)用 Qt 的 GUI 開發(fā)。 鏈接

Python 雖然很好,但是有些功能,并沒有從 C++ 里面暴露出來。

這種情況就需要通過 C++ 的藍(lán)圖開發(fā)來將這部分的功能進(jìn)行暴露。

這樣 Python 基本上可以做任何 Unreal 的事情。

如何開發(fā)藍(lán)圖庫也基本可以參照上篇文章提到的 Unreal Python 教程。 鏈接

為什么需要開發(fā) C++ 藍(lán)圖

上面的視頻鏈接有很詳細(xì)如何通過 Unreal C++ API 開發(fā)一個(gè) Unreal 的藍(lán)圖,暴露給 Python 調(diào)用。

Unreal 的 Python 插件其實(shí)已經(jīng)將 Unreal 內(nèi)置的所有藍(lán)圖暴露給了 Python。

因此 藍(lán)圖 能夠做到的事情, Python 是絕對(duì)可以做到的。

而且經(jīng)過一個(gè)多月的使用來看, Python 的 API 文檔做得比 藍(lán)圖 的 文檔要好很多。

有時(shí)候直接查 Python 的 API 反而更有效率,甚至?xí)l(fā)現(xiàn)一些其他插件所引入的藍(lán)圖。

那么 Python 相較于 藍(lán)圖 的有什么異同?

我目前的使用感受來看,除了失去圖形節(jié)點(diǎn)編程的可視化之外,基本上碾壓藍(lán)圖,當(dāng)然運(yùn)行效率上沒有測(cè)試過。

藍(lán)圖 和 Python 的定位有很大的不一樣。

藍(lán)圖可以作為游戲運(yùn)行邏輯的一部分, Python 只能當(dāng)做編輯器的自動(dòng)化工具。(Python 效率太低了,運(yùn)行腳本寧愿用 lua 調(diào) C++)

藍(lán)圖自身有它的優(yōu)缺點(diǎn),效率比不過 C++ 鏈接

但是圖形化編程,對(duì)于非 coding 人員很友好,而且一些簡(jiǎn)單的邏輯也比較直觀。

但是復(fù)雜藍(lán)圖的連線還是太讓人勸退了。

Python 對(duì)于像我這種工具向開發(fā)的 TA 來說太友好了,畢竟很多 DCC 都使用 Python 。

Unreal 的 Python 插件大部分是對(duì) 藍(lán)圖 的分裝,基本上藍(lán)圖有的功能都可以通過 Python 來調(diào)。

同時(shí) Python 還可以實(shí)現(xiàn)一些神奇的功能,比如說通過 Python 開發(fā)一個(gè)藍(lán)圖節(jié)點(diǎn) ,調(diào)用 Python 的第三方庫諸如 PySide 包,或者調(diào)用系統(tǒng)的 cmd 或者 shell 命令。

因此從引擎的提升來說, Python 的確在這方面更勝一籌,復(fù)雜邏輯通過代碼看也比較直觀。

當(dāng)然很明顯, 藍(lán)圖不能實(shí)現(xiàn)的引擎操作,基本上也不用指望 Python 能夠調(diào)用什么 API 來實(shí)現(xiàn)了。

這種情況下就需要 C++ 來擴(kuò)展藍(lán)圖,實(shí)現(xiàn) Python 調(diào)用。

C++ 開發(fā)藍(lán)圖庫插件

我們目前的需求并不是開發(fā)游戲調(diào)用的藍(lán)圖,因此我們可以開發(fā)一個(gè)藍(lán)圖庫插件。

這樣可以輕易將這些藍(lán)圖遷移到不同的項(xiàng)目里面去。

Unreal 搭建藍(lán)圖庫開發(fā)其實(shí)并不難,按照官方的指引去做即可。

首先需要?jiǎng)?chuàng)建一個(gè) C++ 工程,如果是藍(lán)圖工程是無法寫 C++ 代碼的。

然后打開插件面板,選擇 New Plugin

然后引擎就會(huì)自動(dòng)創(chuàng)建一個(gè)基礎(chǔ)插件的模板出來。

后續(xù)就是在這個(gè)基礎(chǔ)模板上調(diào)用 C++ 的 API 實(shí)現(xiàn)一些特殊的功能。

unreal C++ 插件注意事項(xiàng)

插件的默認(rèn)結(jié)構(gòu)是 .uplugin 文件加 Resource 和 Source 文件夾。

uplugin 就是一個(gè) Json 配置,配置了插件在引擎的插件列表的顯示,以及加載方式。

Resource 存放插件顯示的圖標(biāo)。

Source 存放的是 C++ 源碼了。

前面兩個(gè)不需要太過關(guān)注,重點(diǎn)的 Source 文件夾的東西。

里面會(huì)有 *.Build.cs 文件以及 Public 和 Private 文件夾。

*.Build.cs是 C# 代碼,通過虛幻的 Reflect 機(jī)制生成 Intermediate 的中間代碼用來編譯生成 dll。

Public 默認(rèn)存放頭文件

Private 默認(rèn)存放cpp源碼

引用了引擎內(nèi)部的一些庫,需要在 build.cs 文件里面添加上。

否則編譯的時(shí)候回報(bào)某些類型無法識(shí)別的錯(cuò)誤。

試過排查這種小錯(cuò)誤花了我大半天。

前面兩個(gè)部分是添加路徑的,用來縮短頭文件索引的路徑長(zhǎng)度。

后面的 Private 和 Public Module 則是最重要的索引頭文件的,必須要在這里配置才能在 c++ 里面調(diào)用。

這里怎么填寫可以參考引擎 Source 源碼下的文件夾名稱。

cs 里面配置就可以找 Source 源碼的一些頭文件進(jìn)行引用了。

因?yàn)樘摶瞄_源了,所以內(nèi)部 Private 和 Public 沒有什么區(qū)別,也可能是我的 C++ 造詣還不夠。

配置頭文件就可以愉快地使用官方提供的一些 C++ 了。

C++ 實(shí)現(xiàn) Add Component 藍(lán)圖功能

這個(gè)功能看似非常簡(jiǎn)單,奈何 Python 就是實(shí)現(xiàn)不了。

就是給現(xiàn)有 Actor 添加新的 Component 組件而已。

但是查了 API 文檔,即便使用 Attach 相關(guān)的方法,也無法新的 Component 添加到 Actor 上。

應(yīng)該說 Python 的操作沒有問題, Component 也加上了,可以通過 Python 獲取到,但是 Component 沒有注冊(cè),無法在 UI 上顯示出來。

經(jīng)過我查閱大量網(wǎng)上的資料之后,只在論壇上找到了一個(gè)通過 C++ 實(shí)現(xiàn)的方案。 鏈接

這段代碼里面有很關(guān)鍵的 RegisterComponent 的操作。

而這些操作并沒有暴露到 Python 或者說 藍(lán)圖 里面。

當(dāng)然這個(gè)添加 Component 的方法估計(jì)也和 Unreal 的機(jī)制有關(guān),我對(duì) Unreal 引擎還不是很熟,就不做無關(guān)的揣測(cè)了。

Python 的文檔在 Actor 的部分有所涉及。

不過這個(gè)問題就非常蛋疼,

unreal.EditorLevelLibrary.get_all_level_actors_components 可以獲取所有注冊(cè)的 Component

Actor 也可以刪除現(xiàn)有的 Component ,偏偏無法添加新的 Component

C++ 的部分我簡(jiǎn)化了上面回答的代碼。

如果沒有傳入具體的 Component 類型就返回 None 給 Python 就好了。

UActorComponent* URedArtToolkitBPLibrary::AddComponent(AActor* a, USceneComponent *future_parent, FName name, UClass* NewComponentClass)

{

UActorComponent* retComponent = nullptr;

if (NewComponentClass)

{

UActorComponent* NewComponent = NewObject(a, NewComponentClass, name);

FTransform CmpTransform;// = dup_source->GetComponentToWorld();

//NewComponent->AttachToComponent(sc, FAttachmentTransformRules::KeepWorldTransform);

// Do Scene Attachment if this new Comnponent is a USceneComponent

if (USceneComponent* NewSceneComponent = Cast(NewComponent))

{

if (future_parent != 0)

NewSceneComponent->AttachToComponent(future_parent, FAttachmentTransformRules::KeepWorldTransform);

else

NewSceneComponent->AttachToComponent(a->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform);

NewSceneComponent->SetComponentToWorld(CmpTransform);

}

a->AddInstanceComponent(NewComponent);

NewComponent->OnComponentCreated();

NewComponent->RegisterComponent();

a->RerunConstructionScripts();

retComponent = NewComponent;

}

return retComponent;

}

頭文件怎么去 #include ,我基本就是用 VScode 搜索引擎源碼,查找頭文件的位置,然后逐個(gè)添加。

C++有點(diǎn)麻煩的地方就是 cpp 代碼寫完之后還要將函數(shù)注冊(cè)到 頭文件 里面

不過基本上復(fù)制 cpp 的函數(shù)第一行就可以了,只需要把 :: 前面的類名刪除掉而已。

下面就是點(diǎn)擊 VS 上面的 本地 windows 調(diào)試,編譯插件并啟動(dòng)項(xiàng)目。

我用 VS2017 編譯經(jīng)常遇到 clxx.dll 命令行過長(zhǎng) 的錯(cuò)誤。

網(wǎng)上了查了要將項(xiàng)目的編譯改為 Release 版本,或者升級(jí)到 VS2019 才可以解決。(網(wǎng)上查到這個(gè)是 VS 的 BUG)

后來我是隨便將一些 Intermediate 文件夾刪除,然后重新調(diào)用 UnrealHeaderTool 生成反射代碼就不會(huì)有這個(gè)編譯報(bào)錯(cuò)了。

完成到這里基本可以參照老外的教程,使用 Python 可以在 unreal 庫下找到剛才藍(lán)圖擴(kuò)展的類的,類下面就由剛才擴(kuò)展的 函數(shù) 了。

行數(shù)名稱自動(dòng)將 C++ 的駝峰轉(zhuǎn)為 Python pep8 規(guī)范的 sneak 寫法。

C++ 藍(lán)圖獲取當(dāng)前 Sequencer 選擇的元素

上面介紹了 C++ 的編寫的流程,就不再追溯,這里著重看藍(lán)圖的實(shí)現(xiàn)。

我最近有一個(gè)需求是要獲取當(dāng)前打開的 Sequencer 里面的元素,然后進(jìn)行 FBX 導(dǎo)出。

但是查遍了 Unreal 的 Python 文檔也沒有找到這個(gè)方法。

對(duì)了這里記錄一個(gè)天坑,之前被坑慘了的。

Unreal Python 的老外教程里面也記錄一些使用 Sequencer 處理的 Python 方案。

但是我發(fā)現(xiàn)到我調(diào)用這些 API 的時(shí)候, Unreal 居然報(bào)錯(cuò)找不到這些 API。

然后我就以為是我當(dāng)前 Unreal 版本出 BUG 了,或者是官方刪除了這個(gè)功能。

后來折騰了好久之后才發(fā)現(xiàn),我沒有開啟 Sequencer Scripting 插件,所以那些調(diào)用藍(lán)圖沒有加載(:з」∠)

我當(dāng)時(shí)還不知道 Python 調(diào)用的就是藍(lán)圖, 踩了這個(gè)坑才有了深刻的認(rèn)識(shí)。

回到這里要實(shí)現(xiàn)的功能,我查了 C++ 相關(guān)的問題,總算是找到了一個(gè)比較可靠的回復(fù)。 鏈接

于是我就抄了這里的代碼。

不過上面的代碼有點(diǎn)舊,其中 IAssetEditorInstance* AssetEditor = UAssetEditorSubsystem().FindEditorForAsset(LevelSeq, true); 編譯會(huì)報(bào)錯(cuò)

修改為 IAssetEditorInstance* AssetEditor = GEditor->GetEditorSubsystem()->FindEditorForAsset(LevelSeq, true); 解決問題。

經(jīng)過修改之后上面的代碼可以獲取到當(dāng)前 Sequencer 打開的 LevelSequence

原理也不復(fù)雜,就是遍歷項(xiàng)目所有的 LevelSequence 然后找到那個(gè)開啟了 Editor 的 LevelSequence

然后在從這個(gè) LevelSequence 里獲取 Editor 再從 Editor 獲取 Sequencer。

雖然這個(gè)遍歷有點(diǎn)不太合理,但是我在測(cè)試的項(xiàng)目上還是很奏效的。

但是當(dāng)我將代碼編譯放到我們正在開發(fā)的項(xiàng)目上之后,出現(xiàn)了大問題。

項(xiàng)目有大量的 LevelSequence ,遍歷需要很長(zhǎng)的時(shí)間,并且遍歷之后大量的材質(zhì)啟動(dòng)了編譯,導(dǎo)致電腦很卡。

于是我又查了一下 C++ API 文檔,發(fā)現(xiàn)有個(gè)很有用的函數(shù) GetAllEditedAssets。

這個(gè)函數(shù)可以獲取當(dāng)前打開在編輯器里面的 Assets ,能打開的 Asset 肯定就那么幾個(gè)。

這樣找 Editor 的速度可就快多了。

ULevelSequence* URedArtToolkitBPLibrary::GetFocusSequence()

{

UAssetEditorSubsystem* sub = GEditor->GetEditorSubsystem();

TArray assets = sub->GetAllEditedAssets();

for (UObject* asset : assets)

{

IAssetEditorInstance* AssetEditor = sub->FindEditorForAsset(asset, false);

FLevelSequenceEditorToolkit* LevelSequenceEditor = (FLevelSequenceEditorToolkit*)AssetEditor;

if (LevelSequenceEditor != nullptr)

{

ULevelSequence* LevelSeq = Cast(asset);

return LevelSeq;

}

}

return nullptr;

}

上面只是找 LevelSequence ,還需要找當(dāng)前 LevelSequence 里面選擇的元素。

好在 Sequencer 提供了 GetSelectedObjects 的方法

通過 LevelSequence 可以獲取到 Sequencer

TArray URedArtToolkitBPLibrary::GetFocusBindings(ULevelSequence* LevelSeq)

{

IAssetEditorInstance* AssetEditor = GEditor->GetEditorSubsystem()->FindEditorForAsset(LevelSeq, false);

FLevelSequenceEditorToolkit* LevelSequenceEditor = (FLevelSequenceEditorToolkit*)AssetEditor;

TArray SelectedGuid;

if (LevelSequenceEditor != nullptr)

{

ISequencer* Sequencer = LevelSequenceEditor->GetSequencer().Get();

Sequencer->GetSelectedObjects(SelectedGuid);

return SelectedGuid;

}

return SelectedGuid;

}

這樣獲取返回的是 Guid , Python 有 Guid 類。

可以通過 LevelSequence 的 get_bindings 方法獲取 sequence 相關(guān)的 binding

再調(diào)用 get_id 方法獲取 guid ,然后通過 C++ 的藍(lán)圖將獲取到的 id 篩選一遍。

# NOTE 獲取當(dāng)前 Sequencer 中的 LevelSequence

sequence = unreal.RedArtToolkitBPLibrary.get_focus_sequence()

# NOTE 獲取當(dāng)前 Sequencer 中選中的 Bindings

id_list = unreal.RedArtToolkitBPLibrary.get_focus_bindings(sequence)

bindings_list = [binding for binding in sequence.get_bindings() if binding.get_id() in id_list]

這樣就獲取到了當(dāng)前選擇的 SequencerBindingProxy 類。

通過 unreal.SequencerTools.export_fbx 就可以將選擇的元素導(dǎo)出 FBX 了。

import unreal

from Qt import QtCore, QtWidgets, QtGui

def alert(msg=u"msg", title=u"警告", button_text=u"確定"):

# NOTE 生成 Qt 警告窗口

msg_box = QtWidgets.QMessageBox()

msg_box.setIcon(QtWidgets.QMessageBox.Warning)

msg_box.setWindowTitle(title)

msg_box.setText(msg)

msg_box.addButton(button_text, QtWidgets.QMessageBox.AcceptRole)

unreal.parent_external_window_to_slate(msg_box.winId())

msg_box.exec_()

def unreal_export_fbx(fbx_file):

# NOTE 獲取當(dāng)前 Sequencer 中的 LevelSequence

sequence = unreal.RedArtToolkitBPLibrary.get_focus_sequence()

if not sequence:

alert(u"請(qǐng)打開定序器")

return

# NOTE 獲取當(dāng)前 Sequencer 中選中的 Bindings

id_list = unreal.RedArtToolkitBPLibrary.get_focus_bindings(sequence)

bindings_list = [binding for binding in sequence.get_bindings() if binding.get_id() in id_list]

if bindings_list:

# NOTE 導(dǎo)出 FBX 文件

option = unreal.FbxExportOption()

option.set_editor_property("collision",False)

world = unreal.EditorLevelLibrary.get_editor_world()

unreal.SequencerTools.export_fbx(world,sequence,bindings_list,option,fbx_file)

else:

alert(u"請(qǐng)選擇定序器的元素進(jìn)行 FBX 導(dǎo)出")

return

上面就是完整的示例代碼。

當(dāng)然導(dǎo)出的 FBX 是帶動(dòng)畫的,還需要將動(dòng)畫處理成帶 蒙皮骨骼 的 FBX 。

這個(gè)操作我是通過 FBX Python SDK 實(shí)現(xiàn)的。

官方的 ExportScene01 包含了蒙皮創(chuàng)建,關(guān)鍵幀處理等等的操作,絕大部分的代碼可以照抄。

這里蒙皮轉(zhuǎn)換的需求很簡(jiǎn)單,因此稍微修改一下就可以用了。

處理完成之后將 FBX 輸出到臨時(shí)目錄,然后用 Python 調(diào) windows 命令打開路徑。

總結(jié)

其實(shí)調(diào)用 C++ API 并不難,這種程度的操作還沒有修改到 Unreal 的底層,很多機(jī)制也沒有用到,我作為個(gè)外行還是可以應(yīng)付的。

而且 Unreal C++ 本身做了很多工作,比如實(shí)現(xiàn)了 垃圾回收,含有智能指針,都降低了開發(fā)難度(同時(shí)增加了學(xué)習(xí)的難度)

Unreal 開發(fā)比較難受的地方時(shí)教程文檔各方面都不全, Unity 文檔還有代碼示例,Unreal 因?yàn)殚_源,基本上就是讓你直接看源碼(:з」∠)

有時(shí)候遇到的一些奇奇怪怪的問題還找不到任何網(wǎng)上的提問,就很難受了。

最后引擎編譯非常耗時(shí),如果要搞這一塊的研究,一定一定要配臺(tái)好電腦。

總結(jié)

以上是生活随笔為你收集整理的uasset python_Unreal Python 结合 C++ 开发蓝图库插件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。