UE行为树编辑器图文笔记

news2024/11/26 20:18:09

对UE的编辑器实现有点好奇,于是从比较熟悉的行为树编辑器着手分析。以下为阅读UE源码后的个人理解,如有错误请指正。

编辑器基础

扩展编辑器的几种方式

  • MenuBar 菜单栏
  • ToolBar 工具栏
  • DetailCustomization 自定义细节面板,支持两种方式:IDetailCustomization自定义整个细节面板、 IPropertyTypeCustomization只对特定类型字段进行自定义。
  • GraphNode 节点的扩展。例如对现有蓝图节点的扩充。
  • Custom Asset Editor 自定义一个新的资源类型并拥有独立的编辑器。
  • Custom Edit Mode 自定义编辑模式。类似于:地形编辑模式、植被编辑模式……
  • Editor Utility Widgets
  • Scripted Actions:Create Blueprints that you can invoke by right-clicking an Asset in the Content Browser or an Actor in the Level.

推荐视频教程:

  • 虚幻引擎 4 的编辑器扩展 柴云天
  • Extending the Editor: Making the Most of Unreal Engine’s Existing Framework | Unreal Fest 2024

编辑器核心概念及关键类

  • Menu 菜单栏

  • ToolBar 工具栏

  • Tab tab页

  • TabManager tab页管理器

  • Layout 布局

    • FLayoutNode 布局节点,有一个尺寸系数。
    • FSplitter 布局切分,可横向或者纵向进行切分。继承自FLayoutNode
    • FArea 继承自FSplitter
    • FStack 可以承载多个tab页,但只有一个在前台(激活态)。继承自FLayoutNode
  • FToolkitManager
    单例,管理所有编辑器实例。主要函数:FindEditorForAsset尝试找到资源所对应的编辑器。

  • UAssetEditorSubsystem
    资源编辑器子系统,记录了当前所有正在编辑的资源。主要函数:OpenEditorForAsset打开资源编辑器、FindEditorForAsset查找资源对应的编辑器、GetAllEditedAssets获取所有当前编辑中的资源……

其它

  • UEditorAssetSubsystem:暴露资源相关工具方法给蓝图侧
  • UEditorActorSubsystem:暴露关卡中Actor相关工具方法给蓝图侧

行为树编辑器代码分析

行为树编辑器代码目录结构如下:
在这里插入图片描述

  • FBehaviorTreeEditorModule
    行为树编辑器模块类,提供了创建行为树编辑器的接口,在模块启动的时候注册了对detail面板的自定义、注册了行为树节点对应可视节点的工厂方法、创建了菜单扩展管理器以及工具栏扩展管理器。
  • FBehaviorTreeDebugger
    行为树调试器:提供断点的添加、移除,单步步进等。待进一步详细分析。

行为树编辑器逻辑层(Model)

  • 资源定义
    • UAssetDefinition_BehaviorTree:行为树的资源定义,定义了资源在Content Drawer中的名字的显示、颜色、分类、被双击所执行的操作、文件对比。
    • UAssetDefinition_Blackboard:黑板的资源定义,同上。
  • 工厂类
    • UBehaviorTreeFactory行为树工厂类
    • UBlackboardDataFactory黑板工厂类
  • UBehaviorTreeGraph行为树图,也就是我们平常编辑的行为树的逻辑表示。
  • UBehaviorTreeGraphNode行为树图中节点的基类
    • UBehaviorTreeGraphNode_Root行为树图的根节点
    • UBehaviorTreeGraphNode_Composite行为树图中复合节点
    • UBehaviorTreeGraphNode_SimpleParallel
    • UBehaviorTreeGraphNode_Decorator
    • UBehaviorTreeGraphNode_Service
    • UBehaviorTreeGraphNode_Task
    • UBehaviorTreeGraphNode_SubtreeTask
    • UBehaviorTreeGraphNode_CompositeDecorator复合装饰器节点,里面包含了一个Graph,从而可以通过与或非来组合重利用已有装饰器
      • UBehaviorTreeDecoratorGraph复合装饰器节点的Graph
      • UEdGraphSchema_BehaviorTreeDecorator定义了UBehaviorTreeDecoratorGraph的行为模式
      • UBehaviorTreeDecoratorGraphNode复合装饰器Graph中节点基类
      • UBehaviorTreeDecoratorGraphNode_Logic逻辑节点(与、或、非),继承自UBehaviorTreeDecoratorGraphNode
      • UBehaviorTreeDecoratorGraphNode_Decorator装饰器节点,继承自UBehaviorTreeDecoratorGraphNode
  • UEdGraphSchema_BehaviorTree定义了UBehaviorTreeGraph的行为模式,例如:右键菜单有哪些选项、两个pin是否能够连接、节点的右键菜单列表、连接两个pin……

行为树编辑器控制层(Controller)

  • FBehaviorTreeEditor
  • BehaviorTreeEditorCommands文件中定义了一些命令:行为树的搜索、新建黑板、调试相关的命令(步进、暂停、恢复、停止……)等。
  • BehaviorTreeEditorModes:行为树编辑器的两个工作模式(行为树模式、黑板模式)。模式类中定义了当前模式下Tab的默认布局、注册模式中所有Tab的工厂方法、菜单及工具栏的扩展器。
    • 行为树模式类FBehaviorTreeEditorApplicationMode
    • 黑板模式类FBlackboardEditorApplicationMode
  • BehaviorTreeEditorTabs定义了所有Tab的标识符:行为树Tab、行为树属性面板Tab、行为树搜索面板Tab、黑板属性面板Tab、黑板Key面板。
  • BehaviorTreeEditorTabFactories文件:包含了几个Tab的工厂方法类。
    • FBlackboardEditorSummoner黑板编辑页的工厂类
    • FBlackboardDetailsSummoner黑板detail面板的工厂类
    • FBehaviorTreeDetailsSummoner行为树detail面板的工厂类
    • FBehaviorTreeSearchSummoner行为树搜索面板的工厂类
    • FBTGraphEditorSummoner行为树编辑面板的工厂类
    • FBlackboardSummoner黑板View面板(方便运行中查看黑板的值)的工厂类
  • FBehaviorTreeEditorToolbar负责工具栏

行为树编辑器UI层(View)

  • SBehaviorTreeBlackboardEditor黑板Key编辑面板。
  • SBehaviorTreeBlackboardView黑板条目展示。
  • SGraphNode_BehaviorTree负责单个行为树节点的绘制
  • SGraphNode_Decorator负责复合UBehaviorTreeDecoratorGraphNode_Decorator节点的绘制。
  • BehaviorTreeColors定义了各类节点的颜色、Pin在不同状态下的颜色、调试相关的颜色…
  • BehaviorTreeConnectionDrawingPolicy:行为树种节点之间连线的绘制策略(是否绘制流动的泡泡效果、线的粗线、颜色等)
  • DetailCustomizations文件夹:定制的一些细节面板(黑板detail面板、BlackboardKeySelector)
  • FindInBT负责行为树中的搜索,包括搜索逻辑以及UI。
  • SBehaviorTreeDiff行为树对比界面。

实现细节

入口

从行为树资源被双击打开编辑器作为切入口。
UAssetDefinition_BehaviorTree

EAssetCommandResult UAssetDefinition_BehaviorTree::OpenAssets(const FAssetOpenArgs& OpenArgs) const
{
	// 省略其余代码.....
		if (!bFoundExisting)
		{
			FBehaviorTreeEditorModule& BehaviorTreeEditorModule = FModuleManager::GetModuleChecked<FBehaviorTreeEditorModule>("BehaviorTreeEditor");
			BehaviorTreeEditorModule.CreateBehaviorTreeEditor(Mode, OpenArgs.ToolkitHost, BehaviorTree); // 向行为树编辑器模块儿求情创建行为树编辑器
		}
	}
	return EAssetCommandResult::Handled;
}

行为树编辑器模块创建了FBehaviorTreeEditor

TSharedRef<IBehaviorTreeEditor> FBehaviorTreeEditorModule::CreateBehaviorTreeEditor( const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UObject* Object )
{
    if (!ClassCache.IsValid())
    {
        // 监听行为树蓝图节点类UBTTask_BlueprintBase、UBTDecorator_BlueprintBase、UBTService_BlueprintBase      
    }
    TSharedRef< FBehaviorTreeEditor > NewBehaviorTreeEditor( new FBehaviorTreeEditor() );
    NewBehaviorTreeEditor->InitBehaviorTreeEditor( Mode, InitToolkitHost, Object );
    return NewBehaviorTreeEditor;  
}

FBehaviorTreeEditor定义

class BEHAVIORTREEEDITOR_API FBehaviorTreeEditor : public IBehaviorTreeEditor, 
public FAIGraphEditor, public FNotifyHook
{
    //...
}

编辑器初始化

FBehaviorTreeEditor的初始化函数InitBehaviorTreeEditor

void FBehaviorTreeEditor::InitBehaviorTreeEditor( const EToolkitMode::Type Mode, 
const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* InObject ){
// DocumentManager初始化(DocumentManager主要记录了双击行为树节点打开的蓝图面板)
// 工具栏构建器ToolbarBuilder的创建
// 命令注册(行为树相关命令、行为树调试相关命令、黑板相关命令)
// 基类FAssetEditorToolkit的初始化InitAssetEditor
//  预创建Detail面板
// 行为树调试器的创建和初始化
// 扩展菜单栏、工具栏
// 创建并添加黑板模式FBlackboardEditorApplicationMode、行为树模式FBehaviorTreeEditorApplicationMode
// 预创建黑板编辑器面板
// 命令到回调的映射
// 设置当前的工作模式(行为树模型、黑板模式),注册TabFactory,从编辑器布局ini配置文件中读取布局信息,恢复UI布局(这里会调用前面注册的各个Tab的工厂方法)。
// 重新生成菜单栏、工具栏
}

Tab工厂类被调用时的堆栈:
在这里插入图片描述

设置当前运行模式的时候会恢复模式对应的编辑器UI布局,而布局包含了一组tab标识符,根据tab标识符找到之前注册的tab工厂类,再由工厂类的CreateTabBody函数来创建tab面板。当所有tab面板都创建完毕的时候,编辑器也就完成了创建。

行为树编辑面板

行为树编辑面板的创建代码在FBehaviorTreeEditor::CreateGraphEditorWidget。

/** Create new tab for the supplied graph - don't call this directly, call SExplorer->FindTabForGraph.*/
TSharedRef<SGraphEditor> FBehaviorTreeEditor::CreateGraphEditorWidget(UEdGraph* InGraph)
{
	check(InGraph != NULL);
	
	if (!GraphEditorCommands.IsValid())
	{
		CreateCommandList();

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().RemoveExecutionPin,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnRemoveInputPin ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanRemoveInputPin )
			);

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AddExecutionPin,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnAddInputPin ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanAddInputPin )
			);

		// Debug actions
		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AddBreakpoint,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnAddBreakpoint ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanAddBreakpoint ),
			FIsActionChecked(),
			FIsActionButtonVisible::CreateSP( this, &FBehaviorTreeEditor::CanAddBreakpoint )
			);

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().RemoveBreakpoint,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnRemoveBreakpoint ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanRemoveBreakpoint ),
			FIsActionChecked(),
			FIsActionButtonVisible::CreateSP( this, &FBehaviorTreeEditor::CanRemoveBreakpoint )
			);

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().EnableBreakpoint,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnEnableBreakpoint ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanEnableBreakpoint ),
			FIsActionChecked(),
			FIsActionButtonVisible::CreateSP( this, &FBehaviorTreeEditor::CanEnableBreakpoint )
			);

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().DisableBreakpoint,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnDisableBreakpoint ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanDisableBreakpoint ),
			FIsActionChecked(),
			FIsActionButtonVisible::CreateSP( this, &FBehaviorTreeEditor::CanDisableBreakpoint )
			);

		GraphEditorCommands->MapAction( FGraphEditorCommands::Get().ToggleBreakpoint,
			FExecuteAction::CreateSP( this, &FBehaviorTreeEditor::OnToggleBreakpoint ),
			FCanExecuteAction::CreateSP( this, &FBehaviorTreeEditor::CanToggleBreakpoint ),
			FIsActionChecked(),
			FIsActionButtonVisible::CreateSP( this, &FBehaviorTreeEditor::CanToggleBreakpoint )
			);
	}

	SGraphEditor::FGraphEditorEvents InEvents;
	InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FBehaviorTreeEditor::OnSelectedNodesChanged);
	InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FBehaviorTreeEditor::OnNodeDoubleClicked);
	InEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &FBehaviorTreeEditor::OnNodeTitleCommitted);

	// Make title bar
	TSharedRef<SWidget> TitleBarWidget = 
		SNew(SBorder)
		.BorderImage( FAppStyle::GetBrush( TEXT("Graph.TitleBackground") ) )
		.HAlign(HAlign_Fill)
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			.HAlign(HAlign_Center)
			.FillWidth(1.f)
			[
				SNew(STextBlock)
				.Text(TitleText)
				.TextStyle( FAppStyle::Get(), TEXT("GraphBreadcrumbButtonText") )
			]
		];

	// Make full graph editor
	const bool bGraphIsEditable = InGraph->bEditable;
	return SNew(SGraphEditor)
		.AdditionalCommands(GraphEditorCommands)
		.IsEditable(this, &FBehaviorTreeEditor::InEditingMode, bGraphIsEditable)
		.Appearance(this, &FBehaviorTreeEditor::GetGraphAppearance)
		.TitleBar(TitleBarWidget)
		.GraphToEdit(InGraph)
		.GraphEvents(InEvents)
		.AutoExpandActionMenu(true);
}

可以看到行为树编辑面板复用的SGraphEditor。

节点以及连线的绘制在SGraphEditor子UI SGraphPanel的OnPaint中,代码比较长这里就不放了。

SGraphNode_BehaviorTree负责行为树中单个节点的绘制。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2187358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

西安做网站如何打造出色的企业网站

西安做网站如何打造出色的企业网站 随着数字化时代的到来&#xff0c;企业网站已成为展示企业形象、传播品牌价值的重要平台。在西安&#xff0c;如何打造出色的企业网站呢&#xff1f;以下几点建议可以帮助企业在激烈的竞争中脱颖而出。 **1. 清晰的网站定位** 首先&#xff…

【Godot4.3】匀速和匀变速直线运动粒子

概述 本篇论述&#xff0c;如何用加速度在Godot中控制粒子运动。 匀速和匀变速直线运动的统一 以下是匀变速运动的速度和位移公式&#xff1a; v t v 0 a t x t v 0 t 1 2 a t 2 v_tv_0 at \\ x_tv_0t \frac{1}{2}at^2 vt​v0​atxt​v0​t21​at2 当a 0 时&#xf…

计算机科学英语词汇汇总(下)Computer Science English Complete Vocabulary )

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

0基础学习QT——配置开发环境

大纲 安装Qt配置Visual Studio 2022安装插件配置 测试 Qt框架&#xff0c;以其跨平台、高性能以及丰富的UI组件库而著称&#xff0c;是开发图形用户界面应用程序的理想选择。Visual Studio 2022提供了对Qt项目的深度支持&#xff0c;包括智能代码提示、代码导航、调试工具等&am…

(14)MATLAB莱斯(Rician)衰落信道仿真4

文章目录 前言一、改写莱斯分布概率密度函数的理论值二、仿真代码三、仿真结果总结 前言 本文通过将接收信号总功率设置为1&#xff0c;重写了莱斯衰落信道上接收信号幅度的理论PDF式。然后用MATLAB代码生成了在具有不同莱斯因子K的Ricean平坦衰落信道下接收到的信号样本&…

容器适配器-stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…

PlantUML中的实体关系图

概述 实体关系图&#xff08;Entity Relationship Diagrams&#xff0c;ERD&#xff09;是一种被广泛用于数据库建模的图。 1976年美籍华裔计算机科学家陈品山&#xff08;Peter Chen&#xff09;首次提出了Entity Relationship Modeling&#xff08;实体关系建模&#xff09…

数据结构-单链表的反转

一直在路上 目录 前言一、普通方法二、头插法三、递归法总结 前言 本篇文章介绍反转单链表的三种方法&#xff0c;分别为普通方法、头插法、递归法。 一、普通方法 普通方法是从第一个结点开始反转&#xff0c;然后反转剩余的结点。 普通方法需要保存当前结点的前驱和后继&a…

DevExpress WinForms v24.1新版亮点:富文本编辑器、电子表格组件功能升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

自动驾驶-轨迹拼接

在进行自动驾驶的规划之前&#xff0c;要确定当前帧轨迹规划的起点&#xff0c;这个起点常被误认为是当前车辆的位置&#xff0c;即每次以车辆的当前位置进行轨迹规划&#xff1b;其实不是这样的&#xff0c;直观上&#xff0c;这会导致本次次规划的轨迹同上次规划的轨迹之间是…

如何计算服务需要部署多少台机器?

写在前面 遇到流量激增的性能问题&#xff0c;相信绝大多数人的第一反应不是优化代码而是加机器&#xff01;比如隔壁微博一旦出现爆炸性吃瓜&#xff0c;就会紧急扩机器&#xff0c;防止自己服务被打挂&#xff08;虽然经常被打挂 这篇文章我们就来讲一下如何 计算出一个服务…

项目配置说明

文章目录 一、下载 vscode 并安装相应扩展1.1 下载 vscode1.2 安装扩展 二、git 项目三、git 提交流程3.1 确定要提交的代码 四、git 拉新流程 一、下载 vscode 并安装相应扩展 1.1 下载 vscode vscode 我已经发群里了&#xff0c;或者自己去官网下载也行 1.2 安装扩展 打开…

四舵轮车辆中的舵轮角度计算

对于四舵轮车辆&#xff0c;或者对角线安装的双舵轮车辆来说&#xff0c;当同时存在线速度与角速度的时候&#xff0c;它的两个轮子的角度值是不一样的&#xff0c;而它的角度值与其当时的瞬心相关&#xff08;机器人模型与ICR(Instantaneous Center of Rotation)&#xff09;。…

IP6537_C_30W20V--移动设备快充的得力助手,集成 14 种快充协议的降压 SoC

IP6537_C_30W20V是一款集成同步开关的降压转换器、支 持 14 种输出快充协议、支持 Type-C 输出和 USB PD2.0/PD3.0(PPS)协议的 SoC&#xff0c;为车载充电器、 快充适配器、智能排插提供完整的解决方案。 IP6537_C_30W20V支持 USB Type-C 或者 USB A 输出&#xff0c; 5V 输出功…

火语言RPA流程组件介绍--模拟键盘输入

&#x1f6a9;【组件功能】&#xff1a;在浏览器网页中使用键盘操作模拟输入值 配置预览 配置说明 按键间隔(ms) 支持T或# 输入仅支持整型 两次输入按键的间隔,单位毫秒 输入内容 支持T或# 默认FLOW输入项 需要输入的内容 超时时间 支持T或# 输入仅支持整型 输入的超时时…

我们的赞赏码

每一位粉丝的认可&#xff0c;都是我们前进的动力。欢迎为我们点赞、转发和分享&#xff0c;让我们一起传递美好与快乐&#xff01; 我们真诚地邀请您来赞赏我们&#xff0c;您的认可是我们前进的动力&#xff01; 赞赏我们只要0.99&#xff0c;让我们一起在CSDN增长知识&…

OpenAI 开发者大会!实时语音功能有API了,GPT-4o支持多模态微调,上下文cache功能上线

家人们&#xff01;十一假期第1天&#xff0c; OpenAI一年一度的开发者大会又来了惹&#xff01;今年的开发者大会分成三部分分别在美国、英国、新加坡三个地点举办&#xff0c;刚刚结束的是第一场。 去年的OpenAI开发者大会公布了GPT-4 Turbo和GPTs&#xff0c;今年没有大更新…

【硬件模块】SG90舵机模块

SG90舵机模块实物图 180度舵机&#xff1a;可以控制旋转角度、有角度定位。上电后舵机自动复位到0度&#xff0c;通过一定参数的脉冲信号控制它的角度。 360舵机&#xff1a;不可控制角度&#xff0c;只能控制顺时针旋转、逆时针旋转、停止和调节转速。 如图所示为&#xff1a;…

仅用pygame+python实现植物大战僵尸-----完成比完美更重要

前言 其实这个项目再我上半年就想着做一下的&#xff0c;但是一直拖到现在&#xff0c;我现在深刻的理解到&#xff0c;不要想那么多&#xff0c;先做&#xff0c;因为永远不可能准备好&#xff0c;都是边做边学便准备的&#xff0c;完成比完美更重要&#xff1b;使用python&a…

【YOLO学习】YOLOv2详解

文章目录 1. 概述2. Better2.1 Batch Normalization&#xff08;批归一化&#xff09;2.2 High Resolution Classifier&#xff08;高分辨率分类器&#xff09;2.3 Convolutional With Anchor Boxes&#xff08;带有Anchor Boxes的卷积&#xff09;2.4 Dimension Clusters&…