虛幻4渲染編程(工具篇)【第一卷:開發我們的第一個引擎工具】

虛幻4渲染編程(工具篇)【第一卷:開發我們的第一個引擎工具】

來自專欄虛幻4渲染編程17 人贊了文章

我的專欄目錄:

小IVan:專題概述及目錄?

zhuanlan.zhihu.com圖標

工具篇綜述:

很多時候,我們需要一個工具來輔助我們完成一些功能,比如材質編輯器就是一個很好的例子,可以幫我們快速的預覽到我們製作的材質,可以邊做邊看。很多時候技術美術最多寫一些腳本插件,但是這些腳本插件功能其實還是很有限的。作為我大UE4教的技術美術,怎麼能就此屈服呢,我們就是要寫引擎級別的工具。

下面我們就先從簡單的開始,開發一個能操縱整個關卡編輯器的工具:

先看下效果吧:

我們自己做了一個引擎工具,這個工具有我們自己定義的logo。然後我們有一個界面,這個界面裡面有一些按鈕和命令,這個按鈕可以操作關卡編輯器里的物體。

我這裡先做了一個刪除選中物體的操作,這只是一個演示。其實我們只要知道了方法,想做很多複雜操作都只是時間問題了,這裡只是做個演示。

知道方法之後做其他功能就很簡單了,比如吸地板

然後點下保存就可以把這些數據保存在關卡里。


第一步:

先建立一個引擎用的窗口插件

然後我們就可以在VS中得到如下目錄

我們可以得到如下幾個文件

這個初始模板的代碼結構分析在(大象無形-虛幻引擎程序設計淺析)的第210頁有詳細闡述,我這裡就不贅述了。

第二步:

建立我們工具的內容

當完成第一步,我們點擊插件只能顯示一個空的窗口,我們需要創建我們的按鈕。我們創建一個SEventTest.h文件和SEvent.cpp文件,下面是SEvent.h文件

#include "SEditableTextBox.h"#pragma onceDECLARE_DELEGATE_TwoParams(FTestDelegate, FString, FString);class SEventTest : public SCompoundWidget{public: SLATE_BEGIN_ARGS(SEventTest) {} SLATE_EVENT(FTestDelegate, OnStartTest) SLATE_END_ARGS() void Construct(const FArguments& InArgs);private: FReply OnTestButtonClicked(); FTestDelegate OnTestDelegate; TSharedPtr<SEditableTextBox>TestTextOnePtr; TSharedPtr<SEditableTextBox>TestTextTwoPtr; TSharedPtr<SButton>TestButtonPtr;};

SEvent.cpp

#include "SEventTest.h"#include "STextBlock.h"#define LOCTEXT_NAMESPACE "SEventTest"void SEventTest::Construct(const FArguments& InArgs){ OnTestDelegate = InArgs._OnStartTest; ChildSlot [ SNew(SVerticalBox) //-----------------------------// + SVerticalBox::Slot() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SBox) .HeightOverride(20.0f) .WidthOverride(60.0f) [ SNew(STextBlock) .Text(LOCTEXT("TextDefaultValue0","SnapCamera :")) ] ] + SHorizontalBox::Slot() [ SNew(SBox) .HeightOverride(20.0f) .WidthOverride(150.0f) [ SAssignNew(TestButtonPtr, SButton) .OnClicked(this, &SEventTest::OnTestButtonClicked) .Text(LOCTEXT("Login", "SnapCameraButton")) ] ] ] //-----------------------------// + SVerticalBox::Slot() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SBox) .HeightOverride(20.0f) .WidthOverride(150.0f) [ SNew(STextBlock) .Text(LOCTEXT("TextDefaultValue1", "SecondTestLine")) ] ] ] ];}FReply SEventTest::OnTestButtonClicked(){ //FString usn = TestTextOnePtr->GetText().ToString(); //FString pwd = TestTextTwoPtr->GetText().ToString(); OnTestDelegate.ExecuteIfBound(TEXT("aa"), TEXT("bb")); return FReply::Handled();}#undef LOCTEXT_NAMESPACE

然後再創建WidgetDemo.h和WidgetDemo.cpp

#pragma onceclass SWidgetDemo : public SCompoundWidget{public: SLATE_BEGIN_ARGS(SWidgetDemo){} SLATE_EVENT(FTestDelegate, OnStartTest) SLATE_END_ARGS() void Construct(const FArguments& InArgs); void OnMyTest(FString usn, FString pwd);};

WidgetDemo.cpp

#include "WidgetDemo.h"#include "SEventTest.h"#include "LevelEditorActions.h"#include "Engine/Selection.h"#define LOCTEXT_NAMESPACE "SWidgetdemo"void SWidgetDemo::Construct(const FArguments& InArgs){ ChildSlot .HAlign(HAlign_Left) [ SNew(SEventTest).OnStartTest(this, &SWidgetDemo::OnMyTest) ];}void SWidgetDemo::OnMyTest(FString usn, FString pwd){ FLevelEditorActionCallbacks::SnapObjectToView_Clicked(); for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) { AActor* Actor = Cast<AActor>(*It); Actor->Modify(); Actor->Destroy(); }}#undef LOCTEXT_NAMESPACE

然後在PandaTools,cpp中對OnSpawnPluginTab做如下修改

再啟動引擎能看到我們的工具了

最後再給以下我的工程完整目錄


下面對上述代碼作解釋:

SEvent.h

這裡聲明了一個SEventTest類,派生自Slate的最基礎的SCompoundWidget。這裡我們聲明了一個代理。關於代理如果不太理解了話,可以去看看我下面這篇博客:

從仿函數到std::function再到虛幻4 Delegate?

blog.csdn.net圖標

這裡會保存外部傳進來的方法

構建函數里將按鈕的OnClicked與OnTestButtonClicked函數綁定。

我曾在這裡被繞暈了,再來捋一下這個調用吧。

在SWidgetDemo的construct函數中,我們在SEventTest構造的時候,把函數指針傳進其構造函數

然後在SEventTest的構造函數中,OnTestDelegate與OnMyTest完成綁定

然後再把俺就的OnClicked與OnTestButtonClicked綁定。當我們按動按鍵的時候,回去執行

OnTestButtonClicked。OnTestButtonClicked再去執行和它綁定的函數

此時和OnTestDelegate綁定的是OnMyTest函數於是下面的邏輯就可以執行了

在這個OnMyTest函數中,我們直接去調用GEditor的邏輯得到此時編輯器選中的物體,然後對它進行操作。

在FLevelEditorActionCallbacks中還有很多有趣的命令,我們可以用同樣的方法寫我們的工具邏輯

於是乎以後我們想對我們開發的什麼效果通過工具來調試就十分方便了。以前還碰到有一個怪物管理系統,比如在A據點的怪物戰隊的戰力配置,在B據點怪物的戰力和種類配置,也可以通過這個思路完成。還比如前段時間做的布料模擬效果,因為我的邏輯是在c++層的,我可以通過這種方式,直接在編輯器層設置數值然後把數值set給我的底層類,等等。

下面是吸地板的代碼:

bool SWidgetDemo::RayTracingHit(FVector RayOrigin, FVector RayDirection, float RayMarhingLength, FHitResult& OutHitResoult, AActor* OperatedActor){ const TArray<AActor*>IgActor; FVector startpos = RayOrigin; FVector endpos = RayOrigin + RayDirection * RayMarhingLength; return UKismetSystemLibrary::LineTraceSingle( OperatedActor, startpos, endpos, ETraceTypeQuery::TraceTypeQuery1, false, IgActor, EDrawDebugTrace::Type::None, OutHitResoult, true, FLinearColor::Blue, FLinearColor::Red, 1.0f);}void SWidgetDemo::OnMyTest(FString usn, FString pwd){ //FLevelEditorActionCallbacks::SnapObjectToView_Clicked(); for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) { AActor* Actor = Cast<AActor>(*It); Actor->Modify(); FHitResult OutHit; RayTracingHit(Actor->GetActorLocation(), FVector(0,0,-1), 10000.0f, OutHit, Actor); FVector HitPointLoc = OutHit.Location + OutHit.ImpactNormal * 0.1f; Actor->SetActorLocation(HitPointLoc); }}

本篇只是做了個很簡單的引擎自帶的沒有必要自己再做一遍的功能,旨在跑通整個工具開發流程。下一篇將開始搞一些實用的工具開發實例。

Enjoy it @!


推薦閱讀:

TAG:虛幻4遊戲引擎 | 虛幻引擎 | 遊戲引擎 |