1 - 玩家输入入门

本文将初步介绍UE5中增强输入系统的相关概念,例如输入操作(Input Action)和输入映射上下文(Input Mapping Context)的概念,还会讨论如何通过增强输入系统来处理玩家输入,以及如何让摄像机绕玩家旋转。

增强输入系统

虚幻引擎5现在有两个输入系统:

  • 虚幻4开始使用的 传统输入系统:详见虚幻引擎 4.27 文档

  • 作为插件使用的 增强输入系统

    虚幻引擎5(UE5)项目有时会需要更多高级的输入功能,例如复杂的输入处理,或在运行时重新映射输入按键。增强输入(Enhanced Input) 为开发人员提供了这类功能,并能向上兼容 虚幻引擎4(UE4)的默认输入系统。

    此插件实现了多种功能,例如径向死区、同时按键、上下文输入和优先级安排,并且能够在基于 资产 的环境中,拓展对于原始输入数据的筛选和处理功能。

在这里我们使用增强输入系统,在实际使用之前,需要到“编辑”,“插件”中启用增强输入插件(Enhanced Input)。

输入操作

输入操作(Input Action)是增强输入系统和项目代码之间的通信链接,每个输入操作应该表示用户可以执行的某件事,例如“移动”、“蹲伏”、“开火”等。可以在蓝图或C++代码中添加输入监听器来监听输入操作的状态何时发生变化。

接下来看看细节页面中的 Action 项:

首先是值类型(Value Type),它包括下列项:

  • 数字(bool):用于具有二进制状态的输入操作。例如跳跃操作,玩家要么按下它要么不会按。
  • Axis1D (float):用于在一维中具有标量状态的输入操作,例如在开车时的踩油门操作,可以通过轻推/重推手柄来控制踩油门的程度。
  • Axis2D (Vector2D):用于在二维中具有标量状态的输入操作,例如使用两个轴(前向轴和横向轴)的移动角色操作。
  • Axis3D(Vector):用于在三维中具有标量状态的输入操作,不常用。

然后是触发器(Trigger),用于指定执行此输入操作的关键事件,可组合的事件如下:

  • 弦操作 Chorded Action:只要另一个指定的输入操作被触发,那么该输入操作也会被触发。
  • 按下 Down:当按键超过阈值,每一帧都会触发此操作。
  • 按住 Hold:当按键持续时间超过阈值,此操作被触发。可以指定只触发一次还是每帧都触发。
  • 按住并松开 Hold and Release:当按键持续时间超过阈值,且按键松开后,此操作被触发。
  • 已按下 Pressed:当按键超过阈值,直到松开前只触发一次操作。
  • 脉冲 Pulse:当按键超过阈值,输入操作会以指定的时间间隔触发。可以指定首次脉冲是否触发,以及触发的脉冲次数。
  • 已松开 Released:按键松开时间超过阈值时,该操作被触发。
  • 点按 Tap:按键和松键的时间间隔在指定时间内,且按键超过阈值时,该操作被触发。

最后是修改器(Modifier),用于指定修改如何此输入操作的输入,修改器如下:

  • 盲区 Dead Zone:如果低于下阈值,结果被修改为0;如果高于上阈值,结果被修改为1.
  • 视野缩放 Fov Scaling:输入结果将随Fov的缩放而缩放。
  • 取反 Negate:输入结果将被反转(例如1会变成-1)。
  • 响应曲线-指数 Response Curve-Exponential:对输入结果应用指数曲线。
  • 响应曲线-自定义 Response Curve-User Defined:对输入结果应用用户自定义曲线。
  • 标量 Scalar:对输入结果在每个轴上按指定值缩放。
  • 平滑 Smooth:输入结果将在多个帧之间平滑处理(插值)。
  • 拌合输入轴值 Swizzle Input Axis Values:输入结果的轴顺序将会被切换。
  • 转换为世界空间 To World Space:输入结果的轴会被转换为世界空间。

输入映射上下文

输入映射上下文(Input Mapping Contexts)是输入操作的集合,表示玩家可以处于的特定上下文(如走路时,开车时)。它们描述了给定输入操作的触发规则。

细节页面中的 Mapping 项如下,可见它组织了上面提到的输入操作,并为输入操作指定输入方式:

输入映射上下文的触发器和修改器与输入操作的大致相同,这里不做赘述。

处理玩家输入

跳跃操作

接下来看看增强输入系统是如何处理玩家输入的,例如玩家按下空格后角色就会跳跃:

  1. 硬件输入:玩家按下空格键,游戏引擎会监听这个按键事件。
  2. PlayerInput类:将按下/松开空格键翻译为一个输入操作,如果刚好有对应的输入操作,它将通知所有监听此操作的类(是否刚刚按下、松开或更新),在这里是通知跳跃操作被触发了。
  3. PlayerController类:是第一个接收到按键事件的类。
  4. Pawn类:只要该类(及其子类)被玩家控制器类拥有,就能监听按键事件。

接下来实际操作试试,这里将准备一个玩家角色类,然后将其作为父类给蓝图类使用。

创建好玩家角色类后,开始给蓝图准备要绑定的输入操作和输入映射上下文属性:

// MyCharacter.h
// 该角色的输入映射上下文
UPROPERTY(EditAnywhere, Category = Input)
UInputMappingContext* IC_Character;

// 该角色的跳跃输入操作
UPROPERTY(EditAnywhere, Category = Input)
UInputAction* IA_Jump;

接下来需要在MyCharacter.cpp中绑定监听输入操作:

// MyCharacter.cpp
// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	// 强转为增强输入组件
	UEnhancedInputComponent* EnhancedPlayerInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);

	if (EnhancedPlayerInputComponent != nullptr)
	{
		// 添加输入映射上下文
		APlayerController* PlayerController = Cast<APlayerController>(GetController());
		if (PlayerController != nullptr)
		{
			UEnhancedInputLocalPlayerSubsystem* EnhancedSubsystem =
				ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
			if (EnhancedSubsystem != nullptr)
			{
				EnhancedSubsystem->AddMappingContext(IC_Character, 1);
			}
		}

		// TODO: 还能使用委托(Delegates)监听输入操作
		// 绑定输入操作监听
		EnhancedPlayerInputComponent->BindAction(IA_Jump, ETriggerEvent::Started, this, &ACharacter::Jump);
		EnhancedPlayerInputComponent->BindAction(IA_Jump, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
	}
}

这段代码首先给增强输入系统添加输入映射上下文,通过EnhancedSubSystem->AddMappingContext()实现,该函数的参数解释如下:

  • UInputMappingContext* MappingContext:想要激活的输入映射上下文,这里是IC_Character属性。
  • int32 Priority:输入映射情景(们)的优先级,这里是1。

然后还添加了监听输入操作的逻辑,通过EnhancedPlayerInputComponent->BindAction()实现,该函数的参数解释如下:

  • UInputAction* Action:要监听的输入操作,这里是IA_Jump
  • ETriggerEvent TriggerEvent:触发监听的输入事件,这里选择在触发计算开始时的ETriggerEvent::Started和在出发计算完成后的ETriggerEvent::Completed
  • UserClass* Object:回调函数被调用的对象,这里是this
  • HANDLER_SIG::TUObjectMethodDelegate<UserClass>::FMethodPtrFunc:输入发生时要调用的函数指针,这里分别是&ACharacter::Jump&ACharacter::StopJumping

别忘了创建并设定对应的资产:

然后就能实现角色的跳跃了。

摄像机环绕操作

和跳跃操作类似,首先是输入操作IA_Look

然后是输入映射上下文IC_Character的补充:

对于Y轴输入要注意,首先要把轴次序修改为YXZ,然后取反。

接下来是C++代码逻辑:

// MyCharacter.h
// 该角色的摄像机移动操作
UPROPERTY(EditAnywhere, Category = Input)
UInputAction* IA_Look;

protected:
// 角色摄像机的移动
void Look(const FInputActionValue& Value);
// MyCharacter.cpp
void AMyThirdPersonChar::Look(const FInputActionValue& Value)
{
	FVector2D InputValue = Value.Get<FVector2D>();
	AddControllerYawInput(InputValue.X);
	AddControllerPitchInput(InputValue.Y);
}

void AMyThirdPersonChar::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    ...
    EnhancedPlayerInputComponent->BindAction(IA_Look, ETriggerEvent::Triggered, this, &AMyThirdPersonChar::Look);
    ...
}

其中AddControllerYawInput()AddControllerPitchInput()分别负责围绕Z轴(向左和向右转动)和Y轴(向上和向下看)的旋转输入。

这样就实现了围绕角色转动的摄像机。

参考资料

  • 中文翻译:《UE5 C++ 游戏开发完全学习教程》
  • 英文原版:《Elevating Game Experiences with UE5》
  • 虚幻引擎中的增强输入 | 虚幻引擎 5.5 文档 | Epic Developer Community (epicgames.com)