UE5 与 C++ 入门教程·第一课:角色与 Enhanced Input

news2025/1/10 3:03:27

本文主要围绕 UE5 新的输入系统,手把手从 0 搭建 Unreal 项目,实现角色的基础移动。

重要提示:众所周知,C++ 属于编译型语言,因此动态灵活性不足,不过执行效率高,而蓝图简单灵活,却执行效率低。因此推荐一种开发方式—— 用 C++ 创建基类,蓝图继承 C++ 的基类 ,获得一种折衷的优势。

  • 开发工具:Visual Studio 2022
  • Unreal 版本:5.2.1

创建项目

  1. 新建一个基于 C++ 的空项目。
  2. File -> New Level(或者 Ctrl + N),建立一个 Basic 关卡,保存到新建的 Levels 文件夹中;
  3. Edit -> Project Settings… -> Project -> Maps & Modes 里将 Editor Startup MapGame Default Map 设置为新建的关卡。

角色资产

这里选用的 Epic 商城里的免费资产,Paragon: Lt. Belica。目前用的版本是(写的时候的最新的版本) 5.2.1,目前 Belica 还不支持这个版本,选一个最近的版本 5.1 添加到项目中。

Belica 资产

创建一个 Character 类

正如开头所说,我们希望获得一种相对优势,因此会用 C++ 作为父类,项目中实际使用的时候用创建蓝图类继承自 C++。对于简单的参数和功能就可以在蓝图上定制,对于通用或核心的功能或方法则可以在上级 C++ 类中实现。

Content BrowserC++ Classes 点击右键 New C++ Class... 或者在顶部 Tools -> New C++ Class…

这里并没有放到默认目录,而是放到 Character 目录里。注意,这里生成的 C++ 代码会有一个头文件的问题。

// 由于将类创建到 `Character` 目录下,自动生成的代码是下面的这样
#include "Character/BelicaCharacter.h"

// 去掉 Character/,改成下面这样,否则编译会出错
#include "BelicaCharacter.h"

Spring Arm 与 Camera

角色后面跟着一个相机,叫做 跟随相机(followed camera)。为了控制相机与角色的相对距离,通常还带一个弹簧臂(Spring Arm)。调整弹簧臂就可以调节相机的远近。可以说这是所有角色型游戏的基础。

因此,相机是依附于弹簧臂的。 弹簧臂动,相机就动。

我们需要两个组件,USpringArmComponentUCameraComponent。前缀 U 代表继承自 UObject

BelicaCharacter.h 中添加两个组件:

private:
	UPROPERTY(VisibleAnywhere, Category = Camera)
		class USpringArmComponent* CameraBoom;

	UPROPERTY(VisibleAnywhere, Category = Camera)
		class UCameraComponent* FollowCamera;

这里使用了前向声明的小技巧,对于指针类型,可以声明类型,而不需要引入头文件,这样可以避免影响编译速度。这就是为什么使用 class。当然,如果你愿意的话,也可以单独放到文件的开头进行声明,类似下面这样:

class USpringArmComponent;
class UCameraComponent;

UPROPERTY 是 Unreal 定义的宏。某种意义上也是黑魔法,例如你可以让 private 也可以被访问。UPROPERTY 用途非常广,比如变量复制、序列化、蓝图访问、垃圾回收。本文作为入门教程就不展开了。

BelicaCharacter.h 中使用了前向声明,但在 BelicaCharacter.cpp 中,我们就需要关注类的具体方法,因此就必须引入头文件。要查询类在哪个头文件里,最简单粗暴但有效的方法就是把类名贴入搜索引擎。如果你使用的是 Jetbrains 家的 Rider 进行 Unreal 开发,IDE 会自动帮你补全头文件,就可以不用操心这个问题。(特此声明,本人没有收取任何 Jetbrains 广告费)

所在头文件

#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"

在构造函数中初始化这两个组件,并指明层级关系。

ABelicaCharacter::ABelicaCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
	
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(GetMesh());
	CameraBoom->TargetArmLength = 500.f;
	CameraBoom->bUsePawnControlRotation = true;

	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
	FollowCamera->bUsePawnControlRotation = false;
}
  • Unreal 有自己的一套内存管理机制,不能简单的进行 newdelete。需要使用 CreateDefaultSubobject 这样的模板函数,背后也有比较复杂的分配机制。
  • SetupAttachment 就是用来绑定层级的。可以看到 FollowCamera 绑定到了 CameraBoom 上。而 CameraBoom 绑定的是 GetMesh 返回的角色的骨骼。=
  • TargetArmLength 是弹簧臂的长度
  • bUsePawnControlRotation 是使用 Pawn 控制旋转

到了这里,可以进行编译了。

创建蓝图类

可以基于前面的 C++ 类创建一个蓝图类。

从 C++ 中创建蓝图类

可以看到 C++ 代码的层级关系体现在这里,弹簧臂依附与骨骼(Mesh),跟随相机又依附于弹簧臂。

层级关系

Mesh 设置为我们引入的资产,调整位置和旋转。顺便可以调节一下弹簧臂的高度和长度(不用调跟随相机)。

设置 Belica 网格体

可以将蓝图类拖入到关卡中查看效果。

动画蓝图

Belica 资产里已经做好了一个动画蓝图,可以直接使用。至于什么是动画蓝图,简单理解就是一系列动画组合而成,能根据某种状态(例如用户输入)自动切换和过渡动画的动画控制逻辑。后面如果有动画的文章会展开讲制作动画蓝图(也许鸽了)。

在蓝图添加 Belica 的动画蓝图:

添加动画蓝图

运行一下,会有激动人心的动画效果出现,应该可以看到 Belica 拿起枪自信又警觉的样子。

Input Action

这里会使用 Unreal 新版本的 Input 功能,相比于 UE4,看起来稍微复杂一点点,但优势也比较明显,就是可定制性更高,层次更加分明。Input Action 可以理解定义了值类型,对于代码,主要关注 Action 的值,不关心怎么出现这个值的。至于 Action 怎么产生,由谁产生(键盘、手柄、触控等等),则是 Input Mapping Context 要定义的,也包括值的处理过程。

创建一个 Inputs 文件夹,然后再创建一个 Action 文件夹,创建 Input Action(点进去修改类型)。

  • IA_Jump:处理跳跃,对应 Value TypeDigital(bool)(一次性触发);
  • IA_Move:移动,由于这是个有正负方向,而且上下左右,使用 Axis2D(Vector2D)
  • IA_Look:鼠标移动,也是 Axis2D(Vector2D)

创建 Input Action

Input Action 可以视为 Action 名称(标签)和值类型的映射。

Input Mapping Context

在 Inputs 文件夹中创建 IMC_Belica。注意 Negate 对值进行取反(负)方向。空格和 D 不需要特别配置。Look 的 Nagate 只作用于 Y,将 XZ 的勾选去掉。

Input Mapping Context

Enhanced Input

如果使用 5.x 版本以上,Enhanced Input 已经作为默认的 Input 组件(毕竟已经在编辑器里警告说废弃原有 Input 了),但新的 Input 和原有的 Input 是兼容的。如果你看代码的话,新的 EnhancedInputComponent 其实就是 InputComponent 的子类。不放心的话可以去看一眼。

默认的 Input Component

.Build.cs 中将 EnhancedInput 添加作为依赖(因为这里是空项目起步,不是第三人称模板,所以需要手动添加)。

PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput" });

然后刷新 Visual Studio 项目。可以在编辑器顶部的 Tools -> Refresh Visual Studio 2022 Project。或者在文件夹下,对着 .uproject 右键,生成 Visual Studio project files。

生成 Visual Studio 文件

这一步的目的是让依赖的头文件加入到 Visual Studio,否则编译器会找不到头文件。不过,是可以正常编译的。如果你能忍受红色的波浪线,其实也可以不刷新。

角色中加入 IA 和 IMC

有了弹簧臂和跟随相机的铺垫,这一步其实没有太多要讲的。

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
		class UInputMappingContext* BelicaMappingContext;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
		class UInputAction* JumpAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
		class UInputAction* MoveAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
		class UInputAction* LookAction;

但这有个黑魔法,meta = (AllowPrivateAccess = "true") 让蓝图可以访问到私有变量。

初始化和绑定操作

最终的 Action 需要有一个对应的方法去处理这个操作。注意一下,UE4 的 Input 是区分 Axis 和 Action,但现在都已经统一成为 Action 了。所以,都是 BindAction

还有一个小点,BelicaMappingContextAction 之间其实并 没有严格的初始化顺序的关系。 本质上,这是两层的抽象,Context 主要是外部输入到 Action。Action 则负责最后绑定到操作上。

在 .cpp 引入头文件:

#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"

// ...

// Called to bind functionality to input
void ABelicaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(BelicaMappingContext, 0);
		}
	}

	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{

		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ABelicaCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ABelicaCharacter::Look);

	}

}

跳跃的处理方法是用 ACharacter 自带的处理方法。所以处理 Action 操作,只需要实现 MoveLook

处理值的方法

void ABelicaCharacter::Move(const FInputActionValue& Value)
{
	FVector2D MovementVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

		const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

		AddMovementInput(ForwardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}

void ABelicaCharacter::Look(const FInputActionValue& Value)
{
	FVector2D LookVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		AddControllerYawInput(LookVector.X);
		AddControllerPitchInput(LookVector.Y);
	}
}

需要注意,在头文件中声明时,FInputActionValue 是类型的引用,不能使用前向声明。所以需要在 头文件 中(也就是 BelicaCharacter.h)加入 #include "InputActionValue.h"

GameMode 和 接收输入

将 Action 和 IMC 在角色进行设置,同时接收 Player 0 的输入。

将 Action 和 IMC 设置到角色上

接下去就比较简单了,创建 C++ 项目会自动创建一个 GameMode,直接继承一个蓝图的 GameMode 类。

GameMode类

将 Pawn 设置为 Belica 的蓝图类,可以删除场景内的 Belica。

设置 Belica 作为 Pawn

在 World Settings 中设置这个 GameMode。

设置 GameMode

运行游戏,现在 Belica 可以受键盘和鼠标控制移动了。

对了,如果你觉得不希望只能看到 Belica 的背影,可以在 C++ 的构造函数中设置 bUseControllerRotationYaw = false;,当然更简单的方法是在蓝图直接勾选去掉(这才是蓝图的意义,额,其实本文都可以用蓝图)。

去掉ControllerRotationYaw

小结

从 0 开始,最终实现 UE5 新版的 EnhancedInput。这是一切的开始。不过,令我意外的是 Belica 提供了动画蓝图(突然手动制作的欲望就下降了)。

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

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

相关文章

探索TCC:释放高可用性和弹性事务的潜力

1、TCC简介 分布式事务是指在分布式系统中&#xff0c;多个服务之间需要保证数据的一致性和完整性的场景。传统的单机事务无法满足分布式系统的需求&#xff0c;因此需要引入一种新的事务模型来解决分布式事务问题。 TCC&#xff08;Try-Confirm-Cancel&#xff09;是一种基于…

MySQL的分库分表

分必要不要分库分表&#xff08;通过优化之后还明显影响业务再分&#xff0c;可以通过监控慢查询确定&#xff09; 分库分表的一般条件:单表数据量超过1000w&#xff08;阿里应该是说5000w&#xff09;或者单表数据文件(.ibd)超过20GB&#xff0c;这个很重要&#xff0c;&…

点云配准综述一篇综述《A comprehensive survey on point cloud registration》(翻译)

参照了 2021最新关于点云配准的全面综述 - 知乎&#xff0c;并且加了些自己翻译&#xff0c;全篇的内容可能稍有删减。主要作为个人笔记&#xff0c;阅读了几篇综述&#xff0c;发现这篇是质量较好的&#xff0c;值得花时间细读。 文章分类 文章将配准方法分为了同源配准和不…

JMeter三大重要组件——线程组、取样器、查看结果数(3)

JMeter三大重要组件 一、JMeter三大重要组件——线程组1、作用&#xff1a;JMeter主要通过线程组来运行用户脚本2、在取样器错误后要执行的动作&#xff1a;3、线程属性3、调度器4、setUp线程组和tearDown线程组 二、JMeter三大重要组件——取样器1、基本a、自动重定向和跟随重…

Obsidian多端同步插件LiveSync

网友 Leo 和 Paco反馈&#xff0c;群晖升级到 DSM7.2 &#xff0c;注册表可以搜索镜像&#xff0c;根据 Leo 贴的 /var/packages/Docker/etc/dockerd.json 的内容&#xff0c;DSM7.2 应该是使用了 https://docker.nju.edu.cn 作为注册表镜像&#xff0c;但老苏测试过下面几种情…

易基因:易基因近期染色质免疫共沉淀测序(ChIP-seq)研究成果|项目集锦

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 在生物学研究中&#xff0c;DNA与蛋白质之间的互作&#xff08;DNA-Protein Interactions&#xff0c;DPIs&#xff09;是至关重要的&#xff0c;参与基因的表达、调控、复制、重组和修复…

m 序列(最长线性反馈移位寄存器序列)详解

本专栏包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;https://github.com/timerring/information-theory 】或者公众号【AIShareLab】回复 信息论 获取。 文章目录 m 序列 (最长线性反…

Git进阶系列 | 8. 用Reflog恢复丢失的提交

Git是最流行的代码版本控制系统&#xff0c;这一系列文章介绍了一些Git的高阶使用方式&#xff0c;从而帮助我们可以更好的利用Git的能力。本系列一共8篇文章&#xff0c;这是最后一篇。原文&#xff1a;Using the Reflog to Restore Lost Commits[1] “Reflog”是Git不太为人所…

常见的未授权漏洞批量检测工具

常见的未授权漏洞检测 命令行版已放出支持多线程&#xff0c;批量扫描&#xff0c;指定服务扫描&#xff0c;命令行版地址https://github.com/xk11z/unauthorized_com GUI版unauthorizedV2已更新&#xff0c;可批量ip检测导出结果 项目包含 1 、FTP 未授权访问&#xff08…

SadTalker AI模型使用一张图片与一段音频便可以自动生成视频

SadTalker模型是一个使用图片与音频文件自动合成人物说话动画的开源模型,我们自己给模型一张图片以及一段音频文件,模型会根据音频文件把传递的图片进行人脸的相应动作,比如张嘴,眨眼,移动头部等动作。 SadTalker,它从音频中生成 3DMM 的 3D 运动系数(头部姿势、表情),…

webstorm配置vue开发环境

&#x1f333;&#x1f333;&#x1f333;前言&#xff1a;本文章针对于如何用IDE和webstorm运行一个别人的vue项目进行步骤记录。 &#x1f4d9;参考&#xff1a;(10条消息) idea配置vue开发环境_idea配置vue运行环境_drinkworld的博客-CSDN博客https://blog.csdn.net/drinkwo…

VSCode ssh ubuntu20显示图像界面

1、在vscode中安装 Remote X11(SSH) 2、在本地端安装MobaXterm 点击Settings-->Configurations-->X11&#xff0c;设置如下&#xff1a; 3、在服务端修改 ~/.bashrc文件&#xff0c;在末尾添加 export DISPLAY"192.168.0.201:0.0" 其中引号中内容为本地端IP地…

英伟达股价能否凭借AI进一步上涨到500美元?

来源&#xff1a; 猛兽财经 作者&#xff1a;猛兽财经 猛兽财经在之前的关于英伟达的分析中&#xff08;5月2日&#xff09;&#xff0c;就认为英伟达在人工智能方面的增长潜力还没有完全释放出来&#xff0c;并认为英伟达的股价将会很快涨到300美元&#xff0c;结果到了6月…

代码随想录二刷 day34 | 贪心之1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

1005.K次取反后最大化的数组和 题目链接 解题思路&#xff1a; 两次贪心 如何可以让数组和最大呢&#xff1f; 局部最优&#xff1a;让绝对值大的负数变为正数&#xff0c;当前数值达到最大&#xff0c;整体最优&#xff1a;整个数组和达到最大 如何转变K次正负&#xff0c;让…

CASS打印地形图操作

1、打开地形图&#xff0c;如下&#xff1a; 2、在“工程应用”菜单栏中&#xff0c;选择“查询两点距离及方位”&#xff0c;如下&#xff1a; 3、量取地形图的大致范围大小&#xff0c;如下&#xff1a; 读取图上距离&#xff0c;用于设置纸张大小。 4、点击左上角打印图形&…

【算法题】神奇的斐波那契数列(Fibonacci sequence)、青蛙跳台阶问题、矩阵中的路径

神奇的斐波那契数列和青蛙跳台阶问题 一、神奇的斐波那契数列1.1、题目描述1.2、递归算法1.3、迭代法1.4、小结 二、青蛙跳台阶问题2.1、题目描述2.2、思路2.3、动态规划法2.4、小结 三、矩阵中的路径3.1、题目描述3.2、思路3.3、代码实现3.4、小结 总结 一、神奇的斐波那契数列…

卫星地图应用经典实例项目(7个)

本文会介绍引用一些非常好的卫星地图等相关的应用,一方面给大家增加见识,另一方面会提供一些设计开发的思路以及代码。 文章目录 热气球追踪系统googlemap实现卫星轨迹satvis卫星Cesium系统NASA的worldview系统项目Esri-Satellite-Map基于leaflet的卫星轨迹绘制项目地球当前…

关于Altium Designer 差分线规则设置的方法纠偏

本文适用于AD20以后版本。在AD的原理图及pcb的4年设计学习中&#xff0c;入门课是学校的AD09&#xff0c;简单的两层板绘制。后来因工作需要&#xff0c;就报了培训班&#xff0c;学习了基于AD19的相关使用方法。后来在很多的项目开发中&#xff0c;逐渐发现之前从书本、培训课…

Qt QGraphicsScene模块实现圆点绘制在所有窗体的最前方,实现圆点的“彩色拖尾”效果以及“选中方框”效果

文章目录 前言1、效果2、代码实现2.1 思路2.1.1 “拖尾”效果2.1.2 “选中方框区域”效果2.2 代码 总结 系列文章&#xff1a; &#xff08;一&#xff09;Qt 将某控件、图案绘制在最前面的方法&#xff0c;通过QGraphicsScene模块实现 &#xff08;二&#xff09;Qt QGraphics…

【SCI/SSCI/EI录用案例】仅26天录用,1天见刊

【Unionpub学术】录用/见刊/检索案例&#xff1a;2023年6月10日-2023年6月16日 2区材料类SCI 【期刊简介】IF:3.5-4.0&#xff0c;JCR2区&#xff0c;中科院3区 【检索情况】SCI 在检&#xff0c;正刊 【征稿领域】降解材料及其相关技术的研发&#xff0c;如聚合物基轻质材…