需求
在指定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;
}
}
}
}