当前位置: 首页 > news >正文

UE5-C++入门教程(二)---编写Editor类别的自定义模型实现小球规划路线的可视化

前言

  • 本教程将以图文教程的形式讲述如何快速入门通过C++使用UE5.4进行项目编写。
  • UE5的教程系列
    • 第一期:UE5-C++入门教程(一):使用代码创建一个指定目标的移动小球-CSDN博客
  • UE5与ROS2实战->基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(一)—UnrealCV获取深度+分割图像-CSDN博客
  • 本系列教程默认读者有C++使用基础,故不对一些C++基础知识进行详细讲解
  • 本系列教程使用的是最新的UE5.4.3VS2022作为使用实例
  • 关于本节:上一节我们介绍了如何创建Actor及其Component,并使用自定义MoveComponent完成了制定目标小球的追踪和速度设置,由于每次运行我们都需要开始仿真且需要反复进行编译整个项目,这往往是非常浪费时间和资源的,本节我们引入模型和Editor的概念,在不需要启用仿真和编译整个项目的前提下,我们完成在Editor模型下编写自定义可视化小球运动方向模型

1.基本概念:模板和插件

  • *Modules(模块): 在Unreal Engine中,模块是引擎功能的基本构建块。每个模块封装了一组相关的功能,如渲染、物理、动画、音频等。模块可以是系统级别的,也可以是项目级别的。系统级别的模块通常是引擎的一部分,而项目级别的模块则专门为特定项目定制。

    • 模块可以包含C++源代码、头文件、蓝图文件、配置文件、资源文件等。开发者可以通过添加、修改或删除模块来定制Unreal Engine的功能。模块的这种结构允许开发者只包含他们项目所需的组件,从而优化性能和资源使用。
    • 上次教程我们创建的UE5Turtorial项目就是一个Module请添加图片描述
  • *Plugins(插件): 插件是Unreal Engine中的一个特殊类型的模块,它允许开发者在不修改引擎源代码的情况下添加或扩展功能。插件可以是Epic Games提供的,也可以是第三方开发者创建的。插件通常包含一个或多个模块,它们被打包在一起,可以轻松地在不同的项目之间共享。

    • 插件可以提供从视觉效果到游戏逻辑的各种功能。开发者可以通过Unreal Engine的插件市场下载和安装插件,也可以创建自己的插件并将其分享给其他人。

2.创建自定义模块

2-1 为项目介绍模块–修改.uproject
  • 由于反复进退ue5并且进行重复编译调试连接很麻烦,这里提出一个书写自定义模块的方式,下面我们来不借助编译器和UE5,手动为项目添加一个模块

  • 这里我们随便使用一个编辑器打开你的项目根目录,这里我使用的是VScode

  • 找到项目工程文件夹Source文件夹下你的项目模块,找到你项目的.uproject文件请添加图片描述

  • 找到模组

    • Type用于指定模型的类型,这里介绍两种今天我们会用上的
      • Runtime Module运行时模块是在游戏运行时加载的模块,它们包含游戏逻辑、游戏模式、关卡脚本等。这些模块在游戏启动时加载,并在游戏运行期间保持活动状态。
      • Editor Module:编辑器模块是专门为Unreal Engine编辑器设计的模块,它们包含编辑器工具、自定义资产类型、编辑器扩展等。这些模块只在编辑器中加载和使用。
    • LoadingPhase定义了模块加载的时机。
      • Default:这是默认的加载阶段,模块在引擎初始化后加载。
      • PreLoadingScreen:模块在加载屏幕显示之前加载,通常用于初始化需要快速完成的任务。
      • PostEngineInit:模块在引擎初始化完成后加载,通常用于执行需要引擎完全初始化后才能进行的任务。
"Modules": [{"Name": "UE5Tutorial","Type": "Runtime","LoadingPhase": "Default","AdditionalDependencies": ["Engine"]}
],
  • 创建一个自定义模组,设定为Editor
{"Name": "UE5TutorialEditor","Type": "Editor","LoadingPhase": "PostEngineInit"
}

2-2 为模块添加到编译组中
  • 然后找到Source目录下的.Target.cs文件,添加我们新的模块"UE5TutorialEditor.Target.cs"
// Copyright Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;
using System.Collections.Generic;public class UE5TutorialEditorTarget : TargetRules
{public UE5TutorialEditorTarget( TargetInfo Target) : base(Target){Type = TargetType.Editor;DefaultBuildSettings = BuildSettingsVersion.V5;IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_4;ExtraModuleNames.Add("UE5Tutorial");}
}
  • 添加我们的新模块
//ExtraModuleNames.Add("UE5Tutorial");
ExtraModuleNames.AddRange(new string[] { "UE5Tutorial", "UE5TutorialEditor" });

  • 紧接着我们为新的模块创建一个新的文件夹,注意需要和模块名同名UE5TutorialEditor,并把我们原本项目文件夹下的UE5Tutorial.Build.cs拷贝一份到新的自定义模块的文件夹下,并改名为UE5TutorialEditor.Build.cs请添加图片描述

  • 然后我们打开UE5TutorialEditor.Build.cs,把里头对应的类名修改,并将"UnrealEd","UE5Tutorial"添加至依赖中

    • "UnrealEd" 模块指的是Unreal Engine的编辑器模块。
    • "UE5Tutorial"模块是我们上一节写的移动小球的模块
// Copyright Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;public class UE5TutorialEditor : ModuleRules
{public UE5TutorialEditor(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput","UnrealEd","UE5Tutorial"});PrivateDependencyModuleNames.AddRange(new string[] {  });// Uncomment if you are using Slate UI// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });// Uncomment if you are using online features// PrivateDependencyModuleNames.Add("OnlineSubsystem");// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true}
}

2-3 实现模块类–继承自IModuleInterface
  • IModuleInterface 是一个接口,它定义了一个模块与其他模块或系统交互的标准方式。这个接口允许模块提供一组功能,这些功能可以在模块被加载后由其他模块调用,而不需要直接引用模块的内部实现。

  • IModuleInterface 的主要目的是为了实现模块之间的解耦,使得模块可以独立地开发、测试和更新,同时仍然能够与其他模块进行交互。通过实现 IModuleInterface,一个模块可以公开一组函数,这些函数可以被其他模块在需要时调用。

  • 我们创建如下两个文件
    请添加图片描述

  • UE5TutorialEditor.hpp

#pragma once#include "Engine.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
#include "UnrealEd.h"DECLARE_LOG_CATEGORY_EXTERN(UE5TutorialEditor,All,All)class FUE5TutorialEditorModule:public IModuleInterface
{
//public://virtual void StartupModule()override;//virtual void ShutdownModule()override;
};
  • UE5TutorialEditor.cpp
#include "UE5TutorialEditor.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"#include "UnrealEd.h"IMPLEMENT_GAME_MODULE(FUE5TutorialEditorModule, UE5TutorialEditor);

2-4 编译
  • 回到VS2022,点击绿色透明箭头,开始构建项目,我们会发现代码新的模块的库被创建请添加图片描述

  • 在新开的UE中刷新VS2022请添加图片描述

  • VS2022就有新的模块被添加进来了请添加图片描述

  • 如果有报错,请检查代码拼写,不要漏掉了;或者拼错了函数


2-5 检查

  • 在菜单栏的工具找到调试选择模块请添加图片描述

  • 可以看到我们的模块已经被添加进来请添加图片描述


3.创建自定义可视化UI的ComponentVisualizer

  • 工具里我们新建一个C++类(具体方式上一节我们讲过,这里快速略过)
3-1 创建可视化类
  • 我们创建一个Empty的FMoveComponentVisualizer类,继承自FComponentVisualizer

    • FComponentVisualizer 是 Unreal Engine (UE) 中的一个类,它用于在 UE 编辑器中可视化场景中的组件。组件(Components)是 UE 中的一个核心概念,代表游戏对象(Actors)上的各种功能,如静态网格、骨骼网格、相机、灯光等。
    • FComponentVisualizer 类用于在编辑器中绘制组件的可视表示,这通常包括组件的边界框、碰撞体、骨骼等。开发人员可以使用这个类来创建自定义的可视化效果,以便更好地理解组件在场景中的位置和方向。
  • 这里我们实现基类的虚函数DrawVisualization请添加图片描述

    • const UActorComponent* Component: 指向要绘制的组件的指针。这是一个常量指针,意味着你不会在这个函数中修改组件的状态。
    • const FSceneView* View: 指向当前场景视图的指针。场景视图包含了渲染场景所需的所有信息,如相机设置、视口大小等。这也是一个常量指针。
    • FPrimitiveDrawInterface* PDI: 指向用于绘制原语(primitives)的接口的指针。原语可以是线、点、三角形等,用于在 3D 空间中绘制形状。通过这个接口,你可以添加自定义的绘制调用到场景中。
  • FMoveComponentVisualizer.hpp

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ComponentVisualizer.h"
#include "UE5Tutorial/MoveComponent.h"
/*** */
class UE5TUTORIALEDITOR_API FMoveComponentVisualizer: public FComponentVisualizer
{
public:void DrawVisualization(const UActorComponent* Component,const FSceneView*View,FPrimitiveDrawInterface* PDI )override;
};
  • FMoveComponentVisualizer.cpp
    • 这里我们获取UMoveComponent,如果存在,我们就绘制一条线,起点和终点分别是小球的起点和终点
// Fill out your copyright notice in the Description page of Project Settings.#include "MoveComponentVisualizer.h"void FMoveComponentVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{const UMoveComponent* MoveComponent = Cast<UMoveComponent>(Component);if (MoveComponent){PDI->DrawLine(MoveComponent->StartRelativeLocation,MoveComponent->EndRelativeLocation,FLinearColor::Red,SDPG_Foreground,5.0f);}
}
  • 由于小球的StartRelativeLocationEndRelativeLocation属性是private的,这里我们需要为UMoveComponent添加友元FMoveComponentVisualizer,打开MoveComponent.cpp,添加友元
private:friend class FMoveComponentVisualizer;FVector StartRelativeLocation;UPROPERTY(EditAnywhere)float speed=1.0f;UPROPERTY(EditAnywhere)FVector EndRelativeLocation;FVector CurrentLocation;float stoppingDistance = 1.0f;
3-2 注册模型编辑可视化
  • 我们需要在编辑器中注册和注销一个自定义的组件可视化器 FMoveComponentVisualizer
  • UE5TutorialEditorModule.hpp
#pragma once#include "Engine.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
#include "UnrealEd.h"#include "MoveComponentVisualizer.h"
DECLARE_LOG_CATEGORY_EXTERN(UE5TutorialEditor,All,All)class FUE5TutorialEditorModule:public IModuleInterface
{
public:void StartupModule()override;void ShutdownModule()override;
};
  • UE5TutorialEditorModule.cpp
    • 首先检查 GUnrealEd(全局编辑器指针)是否有效。
    • 如果有效,它会创建一个新的 FMoveComponentVisualizer 实例,并使用 MakeShareable 函数将其转换为共享指针。
    • 它注册这个可视化器,以便它可以在编辑器中用于 UMoveComponent 类型的组件。
#include "UE5TutorialEditor.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"#include "UnrealEd.h"IMPLEMENT_GAME_MODULE(FUE5TutorialEditorModule, UE5TutorialEditor);
void FUE5TutorialEditorModule::StartupModule()
{if (GUnrealEd){TSharedPtr<FMoveComponentVisualizer> MoveVisualizer = MakeShareable(new FMoveComponentVisualizer);if (MoveVisualizer.IsValid()){GUnrealEd->RegisterComponentVisualizer(UMoveComponent::StaticClass()->GetFName(), MoveVisualizer);MoveVisualizer->OnRegister();}}}
void FUE5TutorialEditorModule::ShutdownModule()
{if (GUnrealEd){GUnrealEd->UnregisterComponentVisualizer(UMoveComponent::StaticClass()->GetFName());}}
3-3 编译
  • 我们在调试的模块中找到我们的模块,点击重新编译请添加图片描述

  • 点开输出日志,不出意料的报错了,根据报错提示请添加图片描述

  • 我们关闭实时代码编译请添加图片描述

  • 重新点击重新编译请添加图片描述


4. 结果展示

  • 我们修改Move组件的目标位置,可以发现线也随之改变请添加图片描述

  • 请添加图片描述

  • 值得一提的是,整个过程中我们不需要编译整个项目,也不需要进行仿真,这为我们进行代码调试时非常方便的。

  • 此外这条调试线将在开始仿真的时候会消失,因为这是供我们编译模型下使用,后续我们会介绍如何在运行过程中可视化


小结

  • 本节我们介绍了如何自定义module,并添加了一个Editor的模型,使我们可以在不编译整个项目不开启仿真的前提下可视化小球的规划路线。
  • 感谢大家对本教程的支持,如有错误,欢迎指出。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • npm install报错,解决记录
  • 基于x86 平台opencv的图像采集和seetaface6的人脸朝向姿态估计功能
  • Spring OAuth2授权服务配置示例
  • 真题解析 | CCF CSP-J 2020 入门级 C++语言真题及答案
  • 算法笔记|Day29动态规划II
  • 基于x86 平台opencv的图像采集和seetaface6的人脸特征点功能
  • NextJs - 服务端/客户端组件之架构多样性设计
  • function call使用基础
  • 手把手教你手写单例,六种实现方式一网打尽!
  • 【MySQL进阶之路】oracle 9i的经典测试雇员信息表案例——多表查询
  • WPF Mvvm
  • MySQL集群+Keepalived实现高可用部署
  • Hooks 「 useImperativeHandle 」子组件向父组件暴露方法
  • Dockerfile常用指令详解
  • 在NVIDIA jetson中使用jetson-ffmpeg调用硬件编解码加速处理
  • SegmentFault for Android 3.0 发布
  • 5、React组件事件详解
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Android交互
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • Consul Config 使用Git做版本控制的实现
  • emacs初体验
  • EOS是什么
  • ES6简单总结(搭配简单的讲解和小案例)
  • exif信息对照
  • javascript 总结(常用工具类的封装)
  • JSONP原理
  • swift基础之_对象 实例方法 对象方法。
  • 初识 webpack
  • 从0实现一个tiny react(三)生命周期
  • 从零开始的无人驾驶 1
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 聚类分析——Kmeans
  • 线性表及其算法(java实现)
  • 移动端唤起键盘时取消position:fixed定位
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • Spring Batch JSON 支持
  • 回归生活:清理微信公众号
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • !!java web学习笔记(一到五)
  • !$boo在php中什么意思,php前戏
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (52)只出现一次的数字III
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (已解决)Bootstrap精美弹出框模态框modal,实现js向modal传递数据
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转载)利用webkit抓取动态网页和链接