虚幻4渲染编程(工具篇)【第一卷:开发我们的第一个引擎工具】

我的专栏目录:

小IVan:专题概述及目录​zhuanlan.zhihu.com图标

工具篇综述:

很多时候,我们需要一个工具来辅助我们完成一些功能,比如材质编辑器就是一个很好的例子,可以帮我们快速的预览到我们制作的材质,可以边做边看。很多时候技术美术最多写一些脚本插件,但是这些脚本插件功能其实还是很有限的。作为我大UE4教的技术美术,怎么能就此屈服呢,我们就是要写引擎级别的工具。


下面我们就先从简单的开始,开发一个能操纵整个关卡编辑器的工具:

先看下效果吧:

v2-a4f175e7997c79998c46555a2a81e053_b.jpg

我们自己做了一个引擎工具,这个工具有我们自己定义的logo。然后我们有一个界面,这个界面里面有一些按钮和命令,这个按钮可以操作关卡编辑器里的物体。

我这里先做了一个删除选中物体的操作,这只是一个演示。其实我们只要知道了方法,想做很多复杂操作都只是时间问题了,这里只是做个演示。

v2-ac31c95929f4a0195ee5983dbd7ac593_b.gif

知道方法之后做其他功能就很简单了,比如吸地板

v2-73595ac86787210b40de11e304269209_b.gif

然后点下保存就可以把这些数据保存在关卡里。


第一步:

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

v2-ec4828eeec29d92bcbe7940b82c9320c_b.jpg

然后我们就可以在VS中得到如下目录

v2-c0586df612d4fd3566e8263ce9ebf5ca_b.jpg

我们可以得到如下几个文件

v2-0238e04fa372a2a5784ba1837d68e6d7_b.jpg
v2-49630e3190b21ab211d484eb83042dcb_b.jpg

这个初始模板的代码结构分析在(大象无形-虚幻引擎程序设计浅析)的第210页有详细阐述,我这里就不赘述了。

第二步:

建立我们工具的内容

当完成第一步,我们点击插件只能显示一个空的窗口,我们需要创建我们的按钮。我们创建一个SEventTest.h文件和SEvent.cpp文件,下面是SEvent.h文件

#include "SEditableTextBox.h"

#pragma once

DECLARE_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 once

class 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做如下修改

v2-a791ad59f62d5f5beae49564d382e222_b.jpg

再启动引擎能看到我们的工具了

最后再给以下我的工程完整目录

v2-6b938c0602794ee42870cf5a6fd47fce_b.jpg

下面对上述代码作解释:

SEvent.h

v2-2a04fdffab2f33223620c3cfba6bab55_b.jpg

这里声明了一个SEventTest类,派生自Slate的最基础的SCompoundWidget。这里我们声明了一个代理。关于代理如果不太理解了话,可以去看看我下面这篇博客:

从仿函数到std::function再到虚幻4 Delegate​blog.csdn.net图标

这里会保存外部传进来的方法

v2-8f28bae752e0e5ef059367d3645b3a4d_b.jpg

构建函数里将按钮的OnClicked与OnTestButtonClicked函数绑定。

v2-fc3894b15078299877f12b6a69746569_b.jpg

我曾在这里被绕晕了,再来捋一下这个调用吧。

在SWidgetDemo的construct函数中,我们在SEventTest构造的时候,把函数指针传进其构造函数

v2-62e136b63d15783f429aead34ee55a6e_b.jpg

然后在SEventTest的构造函数中,OnTestDelegate与OnMyTest完成绑定

v2-306f460a3cf946c07c00c9fd18df4cdb_b.jpg

然后再把俺就的OnClicked与OnTestButtonClicked绑定。当我们按动按键的时候,回去执行

OnTestButtonClicked。OnTestButtonClicked再去执行和它绑定的函数

v2-e13025d43b1a97a0ab3a5f1b95039800_b.jpg

此时和OnTestDelegate绑定的是OnMyTest函数于是下面的逻辑就可以执行了

v2-c2ae6d08151fd0a06025d1506f724b9f_b.jpg

在这个OnMyTest函数中,我们直接去调用GEditor的逻辑得到此时编辑器选中的物体,然后对它进行操作。

在FLevelEditorActionCallbacks中还有很多有趣的命令,我们可以用同样的方法写我们的工具逻辑

v2-fad02d38d6e47573a4f55ed3b7335aba_b.jpg

于是乎以后我们想对我们开发的什么效果通过工具来调试就十分方便了。以前还碰到有一个怪物管理系统,比如在A据点的怪物战队的战力配置,在B据点怪物的战力和种类配置,也可以通过这个思路完成。还比如前段时间做的布料模拟效果,因为我的逻辑是在c++层的,我可以通过这种方式,直接在编辑器层设置数值然后把数值set给我的底层类,等等。

下面是吸地板的代码:

v2-b7626962c98184569fb838bc78f9379d_b.jpg
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 @!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值