跳到主内容
虚幻5
文章阅读

UE5.5 C++ 动态绑定按键操作,和动态移除按键操作

2025/12/1690 次阅读14 分钟

需求

在指定AActor附近进行陷阱选择,通过按键N来打开陷阱选择器的UI,当玩家离开这个AActor区域后,给玩家动态移除N键操作绑定

定义按键操作

生命


	//按键操作
	UPROPERTY()
	UInputAction* OpenUIAction;

合适的地方初始化


if (!OpenUIAction)
	{
		OpenUIAction = CreateDefaultSubobject<UInputAction>(TEXT("打开选择陷阱UI面板"));
	}

添加绑定

声明绑定和解绑操作函数


	//绑定按键
	UFUNCTION()
	void BindKeyBoard();

	//移除按键绑定
	UFUNCTION()
	void RemoveBindKey(AAMyGamePlayer* Player);

实现这两个方法



void ASelectTrapRegionActor::BindKeyBoard()
{
	if (!OpenUIAction)
	{
		OpenUIAction = CreateDefaultSubobject<UInputAction>(TEXT("打开选择陷阱UI面板"));
	}
	if (PlayerCharacter)
	{
		const auto MappingContext = PlayerCharacter->DefaultInputMappingContext;
		if (const auto InputComponent = PlayerCharacter->MyInputComponent; MappingContext && InputComponent &&
			OpenUIAction)
		{
			const FKey NKey(EKeys::N);
			MappingContext->MapKey(OpenUIAction, NKey);
			InputComponent->BindAction(OpenUIAction, ETriggerEvent::Completed, this,
			                           &ASelectTrapRegionActor::OpenTrapSelectionUI);
		}
	}
}

void ASelectTrapRegionActor::RemoveBindKey(AAMyGamePlayer* Player)
{
	if (Player)
	{
		const auto MappingContext = PlayerCharacter->DefaultInputMappingContext;
		if (const auto InputComponent = PlayerCharacter->MyInputComponent; InputComponent && OpenUIAction)
		{
			// todo解绑 Action
			for (auto& BindActions = InputComponent->GetActionEventBindings(); const TUniquePtr<FEnhancedInputActionEventBinding>& BindAction : BindActions)
			{
				if (const auto Action = BindAction->GetAction(); Action == OpenUIAction)
				{
					if (const auto Index = BindActions.Find(BindAction); Index != INDEX_NONE)
					{
						MyPrintfDebug("移除 BindAction OpenUIAction");
						InputComponent->RemoveBindingByHandle(BindAction->GetHandle());
						break;
					}
				}
			}
		}

		// 移除映射上下文中的N键映射
		const FKey NKey(EKeys::N);
		auto Mappings = MappingContext->GetMappings();
		for (int32 i = Mappings.Num() - 1; i >= 0; --i)
		{
			if (FEnhancedActionKeyMapping& Mapping = Mappings[i]; Mapping.Action == OpenUIAction && Mapping.Key == NKey)
			{
				Mappings.RemoveAt(i);
				MyPrintfDebug("移除按键映射 BindAction OpenUIAction");
				break;
			}
		}
	}
}

完成

完整的代码

头文件


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "MyCharacter.h"
#include "GameFramework/Actor.h"
#include "Model/FMyTrapInfo.h"
#include "Widget/Trap/TrapListSelectUIWidget.h"
#include "SelectTrapRegionActor.generated.h"

class UBoxComponent;

UCLASS()
class DEMO2_API ASelectTrapRegionActor : public AActor
{
	GENERATED_BODY()

public:
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
	
	ASelectTrapRegionActor();

protected:
	

	//加载数据表格,读取所有可以选择的陷阱
	UFUNCTION()
	void LoadAllTrapInfos();

	// 判断角色是否创建在触发盒体内部
	UFUNCTION()
	void CheckGamePlayerIsInTriggerBox();
	
	virtual void BeginPlay() override;


	//触发盒体
	UPROPERTY()
	UBoxComponent* TriggerBox;


	//UI用户界面
	UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="用户界面",DisplayName="陷阱选择组件")
	TSubclassOf<UUserWidget> WidgetClass;
	
	
	//检查玩家是否在该区域
	bool IsPlayerInRegion;

	// 对玩家角色的引用(假设是单人游戏)
	UPROPERTY()
	AAMyGamePlayer* PlayerCharacter;

	//绑定按键
	UFUNCTION()
	void BindKeyBoard();

	//移除按键绑定
	UFUNCTION()
	void RemoveBindKey(AAMyGamePlayer* Player);


	//按键操作
	UPROPERTY()
	UInputAction* OpenUIAction;


	//选择陷阱组件
	UPROPERTY()
	UTrapListSelectUIWidget* TrapSelectWidget;

	//陷阱列表
	UPROPERTY()
	TArray<FMyTrapInfo> TrapInfoArray;

	//延迟执行 actor检测函数
	FTimerHandle TimerHandle;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// 打开陷阱选择UI
	UFUNCTION()
	void OpenTrapSelectionUI();

	//关闭陷阱选择 ui
	UFUNCTION()
	void CloseTrapSelectionUI() const;

	
};

cpp


// Fill out your copyright notice in the Description page of Project Settings.


#include "Actor/MapRegion/SelectTrapRegionActor.h"

#include "EnhancedInputComponent.h"
#include "Blueprint/UserWidget.h"
#include "Components/BoxComponent.h"
#include "Tool/MyLogTool.h"
#include "Widget/Trap/TrapListSelectUIWidget.h"

/// 
ASelectTrapRegionActor::ASelectTrapRegionActor()
{
	PrimaryActorTick.bCanEverTick = true;

	//创建触发盒体
	if (!TriggerBox)
	{
		TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
		TriggerBox->SetGenerateOverlapEvents(true); //生成时触发碰撞检测
		RootComponent = TriggerBox;
		TriggerBox->SetBoxExtent(FVector(700.0f, 700.0f, 200.0f));
		TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly); // 只进行查询碰撞
		TriggerBox->SetCollisionProfileName(TEXT("Trigger"));
		TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ASelectTrapRegionActor::OnOverlapBegin);
		TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ASelectTrapRegionActor::OnOverlapEnd);
	}

	if (!OpenUIAction)
	{
		OpenUIAction = CreateDefaultSubobject<UInputAction>(TEXT("打开选择陷阱UI面板"));
	}

	IsPlayerInRegion = false;
	TrapSelectWidget = nullptr;
}

void ASelectTrapRegionActor::LoadAllTrapInfos()
{
	for (auto Arr = GetTrapAllDataList(); const auto Trap : Arr)
	{
		TrapInfoArray.Add(*Trap);
	}
}

void ASelectTrapRegionActor::CheckGamePlayerIsInTriggerBox()
{
	if (TriggerBox)
	{
		TArray<AActor*> Actors;
		TriggerBox->GetOverlappingActors(Actors, AAMyGamePlayer::StaticClass());
		for (const auto Actor : Actors)
		{
			if (const auto Player = Cast<AAMyGamePlayer>(Actor))
			{
				OnOverlapBegin(nullptr, Player, nullptr, 0, false, FHitResult());
			}
		}
	}
}

void ASelectTrapRegionActor::BeginPlay()
{
	Super::BeginPlay();
	LoadAllTrapInfos();
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &ASelectTrapRegionActor::CheckGamePlayerIsInTriggerBox,
	                                       1.f, false);
}

void ASelectTrapRegionActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

/// 
void ASelectTrapRegionActor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
                                            UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool
                                            bFromSweep, const FHitResult& SweepResult)
{
	if (const auto Player = Cast<AAMyGamePlayer>(OtherActor))
	{
		MyPrintfDebug("进入选择陷阱碰撞区域==========");
		UMyLogTool::DebugToScreen(UTF8_TO_TCHAR("进入陷阱选择区域。"));
		PlayerCharacter = Player;
		IsPlayerInRegion = true;
		BindKeyBoard();
	}
}

/// 
void ASelectTrapRegionActor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
                                          UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (auto Player = Cast<AAMyGamePlayer>(OtherActor))
	{
		MyPrintfDebug("离开选择陷阱碰撞区域==========");
		UMyLogTool::DebugToScreen(UTF8_TO_TCHAR("离开陷阱选择区域"));
		IsPlayerInRegion = false;
		CloseTrapSelectionUI();
		RemoveBindKey(Player);
	}
}

void ASelectTrapRegionActor::OpenTrapSelectionUI()
{
	MyPrintfDebug("触发陷阱选择面板UI");
	if (TrapSelectWidget && TrapSelectWidget->IsInViewport())
	{
		TrapSelectWidget->RemoveFromParent();
		TrapSelectWidget = nullptr;
		return;
	}
	if (UWorld* World = GetWorld(); WidgetClass)
	{
		MyPrintfDebug("打开UI");
		const auto TrapSelectionUI = CreateWidget<UTrapListSelectUIWidget>(World, WidgetClass);
		// Replace YOUR_UI_CLASS with the actual UI class
		TrapSelectWidget = TrapSelectionUI;
		if (TrapSelectionUI)
		{
			TrapSelectionUI->Setup(TrapInfoArray); //设置数据
			TrapSelectionUI->AddToViewport();
		}
	}
}

// 
void ASelectTrapRegionActor::CloseTrapSelectionUI() const
{
	if (UWorld* World = GetWorld())
	{
		MyPrintfDebug("关闭陷阱选择UI");
		if (TrapSelectWidget)
		{
			TrapSelectWidget->RemoveFromParent();
		}
	}
}


void ASelectTrapRegionActor::BindKeyBoard()
{
	if (!OpenUIAction)
	{
		OpenUIAction = CreateDefaultSubobject<UInputAction>(TEXT("打开选择陷阱UI面板"));
	}
	if (PlayerCharacter)
	{
		const auto MappingContext = PlayerCharacter->DefaultInputMappingContext;
		if (const auto InputComponent = PlayerCharacter->MyInputComponent; MappingContext && InputComponent &&
			OpenUIAction)
		{
			const FKey NKey(EKeys::N);
			MappingContext->MapKey(OpenUIAction, NKey);
			InputComponent->BindAction(OpenUIAction, ETriggerEvent::Completed, this,
			                           &ASelectTrapRegionActor::OpenTrapSelectionUI);
		}
	}
}

void ASelectTrapRegionActor::RemoveBindKey(AAMyGamePlayer* Player)
{
	if (Player)
	{
		const auto MappingContext = PlayerCharacter->DefaultInputMappingContext;
		if (const auto InputComponent = PlayerCharacter->MyInputComponent; InputComponent && OpenUIAction)
		{
			// todo解绑 Action
			for (auto& BindActions = InputComponent->GetActionEventBindings(); const TUniquePtr<FEnhancedInputActionEventBinding>& BindAction : BindActions)
			{
				if (const auto Action = BindAction->GetAction(); Action == OpenUIAction)
				{
					if (const auto Index = BindActions.Find(BindAction); Index != INDEX_NONE)
					{
						MyPrintfDebug("移除 BindAction OpenUIAction");
						InputComponent->RemoveBindingByHandle(BindAction->GetHandle());
						break;
					}
				}
			}
		}

		// 移除映射上下文中的N键映射
		const FKey NKey(EKeys::N);
		auto Mappings = MappingContext->GetMappings();
		for (int32 i = Mappings.Num() - 1; i >= 0; --i)
		{
			if (FEnhancedActionKeyMapping& Mapping = Mappings[i]; Mapping.Action == OpenUIAction && Mapping.Key == NKey)
			{
				Mappings.RemoveAt(i);
				MyPrintfDebug("移除按键映射 BindAction OpenUIAction");
				break;
			}
		}
	}
}

返回顶部