Skip to content

Instantly share code, notes, and snippets.

@SkylakeOfficial
Created October 19, 2023 17:44
Show Gist options
  • Select an option

  • Save SkylakeOfficial/292e5d9748eb619e2f1e58d7cf480395 to your computer and use it in GitHub Desktop.

Select an option

Save SkylakeOfficial/292e5d9748eb619e2f1e58d7cf480395 to your computer and use it in GitHub Desktop.
AFPInteractableComponent
#include "ArknightsFP/Interaction/AFPInteractable.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Slate/SlateBrushAsset.h"
#define ENSURE_INTERACT(x) (Cast<IAFPInteractionInterface>(x))
#define WGT (Cast<UAFPInteractWidget>(DisplayWidget->GetWidget()))
class USlateBrushAsset;
// Sets default values for this component's properties
UAFPInteractable::UAFPInteractable(): DisplayWidget(nullptr), RevealTrigger(nullptr), InteractTrigger(nullptr), PlayerPawn(nullptr)
{
//Setup tick stuff
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.TickInterval = 0.2f;
static ConstructorHelpers::FObjectFinder<USlateBrushAsset> WGTBrush(TEXT("/Script/Engine.SlateBrushAsset'/Game/UI/PlayerHUD/HUD_WGTS/InteractableDefaultBrush.InteractableDefaultBrush'"));
if (WGTBrush.Succeeded())
{
WidgetBrush = WGTBrush.Object->Brush;
}
static ConstructorHelpers::FObjectFinder<UTexture2D> BillboardTex(TEXT("/Script/Engine.Texture2D'/Game/GameLogic/Interactable/Tx_Interactable.Tx_Interactable'"));
if (BillboardTex.Succeeded())
{
this->Sprite = BillboardTex.Object;
}
this->ScreenSize = 0.0008f;
this->bIsScreenSizeScaled = true;
}
// Called when the game starts
void UAFPInteractable::BeginPlay()
{
Super::BeginPlay();
RegisterComponent();
//Initialize my three components
RevealTrigger = NewObject<USphereComponent>(this->GetOwner(), USphereComponent::StaticClass(), FName(this->GetName().Append("Reveal")));
InteractTrigger = NewObject<USphereComponent>(this->GetOwner(), USphereComponent::StaticClass(), FName(this->GetName().Append("Interact")));
DisplayWidget = NewObject<UWidgetComponent>(this->GetOwner(), UWidgetComponent::StaticClass(), FName(this->GetName().Append("Widget")));
DisplayWidget->SetWidgetSpace(EWidgetSpace::Screen);
DisplayWidget->SetWidgetClass(WidgetClass);
DisplayWidget->SetDrawSize(FVector2D(128, 128));
DisplayWidget->InitWidget();
DisplayWidget->RegisterComponent();
DisplayWidget->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, false));
WGT->SetBrush(WidgetBrush);
WGT->bUseFirstSight = bUseFirstSight;
RevealTrigger->RegisterComponent();
InteractTrigger->RegisterComponent();
RevealTrigger->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, false));
InteractTrigger->AttachToComponent(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, false));
RevealTrigger->ShapeColor = FColor::Cyan;
InteractTrigger->ShapeColor = FColor::Cyan;
RevealTrigger->SetSphereRadius(RevealDist);
InteractTrigger->SetSphereRadius(InteractDist);
RevealTrigger->SetVisibility(false);
InteractTrigger->SetVisibility(false);
RevealTrigger->SetCollisionObjectType(ECC_PhysicsBody);
InteractTrigger->SetCollisionObjectType(ECC_PhysicsBody);
RevealTrigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
InteractTrigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
FCollisionResponseContainer ResponseContainer;
ResponseContainer.SetAllChannels(ECR_Ignore);
ResponseContainer.SetResponse(ECC_Pawn, ECR_Overlap);
InteractTrigger->SetCollisionResponseToChannels(ResponseContainer);
RevealTrigger->SetCollisionResponseToChannels(ResponseContainer);
InteractTrigger->OnComponentBeginOverlap.AddDynamic(this, &UAFPInteractable::OnInteractTriggerIn);
RevealTrigger->OnComponentBeginOverlap.AddDynamic(this, &UAFPInteractable::OnRevealTriggerIn);
InteractTrigger->OnComponentEndOverlap.AddDynamic(this, &UAFPInteractable::OnInteractTriggerOut);
RevealTrigger->OnComponentEndOverlap.AddDynamic(this, &UAFPInteractable::OnRevealTriggerOut);
if (!bExplicit)
{
RevealTrigger->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
FTimerHandle InitialOverlapCheckHandle;
GetWorld()->GetTimerManager().SetTimer(InitialOverlapCheckHandle, this, &UAFPInteractable::PerformInitialOverlapCheck, 0.5f, false, 0.2f);
}
void UAFPInteractable::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (PlayerPawn)
{
FVector Extent = PlayerPawn->GetActorLocation().operator-(this->GetComponentLocation());
Extent.Normalize(0.0001f);
FVector TraceEnd = PlayerPawn->GetActorLocation().operator+(FVector(0, 0, 50.0)).operator+(Extent.operator*(100.0));
FHitResult Result;
UKismetSystemLibrary::LineTraceSingle(this, this->GetComponentLocation(), TraceEnd, TraceTypeQuery1, false, {this->GetOwner()}, EDrawDebugTrace::None, Result, false);
if (Result.GetActor() == PlayerPawn) //命中玩家
{
if (!bInInteractRange)
{
if (bInRevealRange)
{
switch (InteractState)
{
default: break;
case EInteractState::Hidden:
InteractState = EInteractState::Revealed;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Revealed);
}
break;
case EInteractState::Focused:
InteractState = EInteractState::Revealed;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Revealed);
}
break;
}
}
else
{
this->SetComponentTickEnabled(false);
InteractState = EInteractState::Hidden;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Hidden);
}
}
}
}
else //未命中玩家,则不显示、不可交互
{
switch (InteractState)
{
default: break;
case EInteractState::Focused:
ENSURE_INTERACT(PlayerPawn)->RemoveInteractable(this);
break;
case EInteractState::Revealed:
InteractState = EInteractState::Hidden;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Hidden);
}
break;
}
}
}
}
void UAFPInteractable::PlayFirstSightAnim()
{
if (WGT)
{
WGT->PlayFisrtSight();
}
}
void UAFPInteractable::Interact(APawn* Interactor)
{
OnInteract.Broadcast(Interactor, this);
}
void UAFPInteractable::CancelInteract(APawn* Interactor)
{
OnCancelInteract.Broadcast(Interactor, this);
}
void UAFPInteractable::DeleteInteract()
{
if (!PlayerPawn)
{
return;
}
RevealTrigger->DestroyComponent();
InteractTrigger->DestroyComponent();
DisplayWidget->DestroyComponent();
ENSURE_INTERACT(PlayerPawn)->RemoveInteractable(this);
this->DestroyComponent();
}
void UAFPInteractable::SetCanInteract(bool NewCanInteract)
{
if (bCanInteract && !NewCanInteract)
{
this->SetComponentTickEnabled(false);
RevealTrigger->SetCollisionEnabled(ECollisionEnabled::NoCollision);
InteractTrigger->SetCollisionEnabled(ECollisionEnabled::NoCollision);
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Hidden);
}
ENSURE_INTERACT(PlayerPawn)->RemoveInteractable(this);
bCanInteract = false;
}
if (!bCanInteract && NewCanInteract)
{
RevealTrigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
InteractTrigger->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
bCanInteract = true;
PerformInitialOverlapCheck();
}
}
void UAFPInteractable::Focus()
{
InteractState = EInteractState::Focused;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Focused);
}
OnFocus.Broadcast(this);
}
void UAFPInteractable::UnFocus()
{
if (bInInteractRange || bInRevealRange)
{
InteractState = EInteractState::Revealed;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Revealed);
}
}
else
{
InteractState = EInteractState::Hidden;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Hidden);
}
}
OnUnfocus.Broadcast(this);
}
void UAFPInteractable::SetInteractInfo(FText Name, FText Desc, UTexture2D* Image, FIntPoint Grids, int32 ImgIndex)
{
InteractionText = Name;
DescriptionText = Desc;
DescriptionImage = Image;
TexIndex = ImgIndex;
if (PlayerPawn && InteractState == EInteractState::Focused)
{
ENSURE_INTERACT(PlayerPawn)->RemoveInteractable(this);
ENSURE_INTERACT(PlayerPawn)->AddInteractable(this);
}
}
void UAFPInteractable::OnRevealTriggerIn(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (PlayerPawn)
{
if (OtherActor != PlayerPawn)
{
return;
}
}
else
{
if (!ENSURE_INTERACT(OtherActor))
{
return;
}
PlayerPawn = Cast<APawn>(OtherActor);
}
if (!RevealToken.IsNone())
{
if (!ENSURE_INTERACT(PlayerPawn)->GetPlayerTokens().Contains(RevealToken))
{
return;
}
}
bInRevealRange = true;
this->SetComponentTickEnabled(true);
OnPlayerEnterRevealRange.Broadcast(PlayerPawn,this);
if (WGT)
{
if (WGT->GetRenderOpacity() < 0.9f)
{
if (InteractToken.IsNone())
{
WGT->SetRenderOpacity(1.0f);
}
else if (ENSURE_INTERACT(PlayerPawn)->GetPlayerTokens().Contains(InteractToken))
{
WGT->SetRenderOpacity(1.0f);
}
}
}
}
void UAFPInteractable::OnInteractTriggerIn(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (PlayerPawn)
{
if (OtherActor != PlayerPawn)
{
return;
}
}
else
{
if (!ENSURE_INTERACT(OtherActor))
{
return;
}
PlayerPawn = Cast<APawn>(OtherActor);
}
if (!InteractToken.IsNone())
{
if (!ENSURE_INTERACT(PlayerPawn)->GetPlayerTokens().Contains(InteractToken))
{
if (WGT)
{
WGT->SetRenderOpacity(0.5f);
}
return;
}
}
bInInteractRange = true;
if (WGT)
{
WGT->SetRenderOpacity(1.0f);
}
this->SetComponentTickEnabled(true);
ENSURE_INTERACT(PlayerPawn)->AddInteractable(this);
InteractState = EInteractState::Revealed;
if (UAFPInteractWidget* Widg = WGT)
{
Widg->SetInteractVisibility(EInteractState::Revealed);
}
OnPlayerEnterInteractRange.Broadcast(PlayerPawn,this);
}
void UAFPInteractable::OnRevealTriggerOut(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (PlayerPawn)
{
if (OtherActor != PlayerPawn)
{
return;
}
}
else
{
if (!ENSURE_INTERACT(OtherActor))
{
return;
}
PlayerPawn = Cast<APawn>(OtherActor);
}
OnPlayerExitRevealRange.Broadcast(PlayerPawn, this);
bInRevealRange = false;
}
void UAFPInteractable::OnInteractTriggerOut(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (PlayerPawn)
{
if (OtherActor != PlayerPawn)
{
return;
}
}
else
{
if (!ENSURE_INTERACT(OtherActor))
{
return;
}
PlayerPawn = Cast<APawn>(OtherActor);
}
ENSURE_INTERACT(PlayerPawn)->RemoveInteractable(this);
if (!InteractToken.IsNone())
{
if (!ENSURE_INTERACT(PlayerPawn)->GetPlayerTokens().Contains(InteractToken))
{
if (WGT)
{
WGT->SetRenderOpacity(0.5f);
}
return;
}
}
bInInteractRange = false;
OnPlayerExitInteractRange.Broadcast(PlayerPawn, this);
}
void UAFPInteractable::PerformInitialOverlapCheck()
{
TArray<AActor*> InRevealActors;
RevealTrigger->GetOverlappingActors(InRevealActors, APawn::StaticClass());
TArray<AActor*> InInteractActors;
InteractTrigger->GetOverlappingActors(InInteractActors, APawn::StaticClass());
for (int i = 0; i< InRevealActors.Num();i++)
{
/*if (InInteractActors.Contains(InRevealActors[i]))
{
continue;
}*/
OnRevealTriggerIn(nullptr,InRevealActors[i], nullptr, 0, false, FHitResult());
}
for (auto InInteractActor : InInteractActors)
{
OnInteractTriggerIn(nullptr, InInteractActor, nullptr, 0, false, FHitResult());
}
}
#pragma once
#include "CoreMinimal.h"
#include "Components/BillboardComponent.h"
#include "Components/SphereComponent.h"
#include "Components/WidgetComponent.h"
#include "GameFramework/Pawn.h"
#include "AFPInteractionInterface.h"
#include "AFPInteractWidget.h"
#include "Engine/EngineTypes.h"
#include "AFPInteractable.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInteractSignature, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCancelInteractSiganture, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FInRevealRangeSIgnature, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOutRevealRangeSIgnature, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FInInteractRangeSIgnature, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOutInteractRangeSIgnature, APawn*, Interactor,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFocusSignature,UAFPInteractable*, Comp);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUnfocusSignature,UAFPInteractable*, Comp);
UCLASS(BlueprintType, ClassGroup=(AFPGameplay), meta=(BlueprintSpawnableComponent))
class ARKNIGHTSFP_API UAFPInteractable : public UBillboardComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UAFPInteractable();
UPROPERTY(EditAnywhere, BlueprintReadOnly,DisplayName = "揭露距离")
float RevealDist = 1200;
UPROPERTY(EditAnywhere, BlueprintReadOnly,DisplayName = "交互距离")
float InteractDist = 160;
//第一眼动画,用于重要交互物的强调。该动画可手动调用
UPROPERTY(EditAnywhere,BlueprintReadOnly,DisplayName = "使用first Sight动画")
bool bUseFirstSight = false;
//是否在揭露距离内显示?若false,则仅在交互距离内显示
UPROPERTY(EditAnywhere, BlueprintReadOnly,DisplayName = "显式")
bool bExplicit = true;
UPROPERTY(EditAnywhere, BlueprintReadOnly,DisplayName = "揭露Token")
FName RevealToken;
UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName = "交互Token")
FName InteractToken;
UPROPERTY(EditAnywhere, BlueprintReadOnly,DisplayName = "控件笔刷")
FSlateBrush WidgetBrush;
UPROPERTY(BlueprintReadOnly)
EInteractState InteractState = EInteractState::Hidden;
UPROPERTY(EditAnywhere,BlueprintReadOnly,DisplayName = "控件类")
TSubclassOf<UAFPInteractWidget> WidgetClass = UAFPInteractWidget::StaticClass();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FText InteractionText;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FText DescriptionText;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UTexture2D* DescriptionImage=nullptr;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FIntPoint TexGrids=FIntPoint(1,1);
UPROPERTY(EditAnywhere, BlueprintReadOnly)
int32 TexIndex = 0;
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintAssignable, DisplayName="交互")
FOnInteractSignature OnInteract;
UPROPERTY(BlueprintAssignable, DisplayName="取消交互")
FOnCancelInteractSiganture OnCancelInteract;
UPROPERTY(BlueprintAssignable, DisplayName="进入揭露范围")
FInRevealRangeSIgnature OnPlayerEnterRevealRange;
UPROPERTY(BlueprintAssignable, DisplayName="离开揭露范围")
FOutRevealRangeSIgnature OnPlayerExitRevealRange;
UPROPERTY(BlueprintAssignable, DisplayName = "进入交互范围")
FInInteractRangeSIgnature OnPlayerEnterInteractRange;
UPROPERTY(BlueprintAssignable, DisplayName = "离开交互范围")
FOutInteractRangeSIgnature OnPlayerExitInteractRange;
UPROPERTY(BlueprintAssignable, DisplayName="聚焦")
FOnFocusSignature OnFocus;
UPROPERTY(BlueprintAssignable, DisplayName="取消聚焦")
FOnUnfocusSignature OnUnfocus;
//高亮动画
UFUNCTION(BlueprintCallable,DisplayName = "播放FirstSight动画", Category = "Interact")
void PlayFirstSightAnim();
//与组件交互
UFUNCTION(BlueprintCallable, DisplayName = "交互",Category = "Interact")
void Interact(APawn* Interactor);
//取消组件交互
UFUNCTION(BlueprintCallable, DisplayName = "取消组件交互",Category = "Interact")
void CancelInteract(APawn* Interactor);
//清除交互组件
UFUNCTION(BlueprintCallable, DisplayName = "清除交互组件",Category = "Interact")
void DeleteInteract();
//设置可交互
UFUNCTION(BlueprintCallable, DisplayName = "设置可交互性",Category = "Interact")
void SetCanInteract(bool NewCanInteract);
//
UFUNCTION(BlueprintCallable, DisplayName = "聚焦")
void Focus();
UFUNCTION(BlueprintCallable, DisplayName = "取消聚焦")
void UnFocus();
UFUNCTION(BlueprintCallable, DisplayName = "设置交互信息")
void SetInteractInfo(FText Name, FText Desc, UTexture2D* Image, FIntPoint Grids, int32 ImgIndex = 0);
protected:
//交互的UI组件
UPROPERTY()
UWidgetComponent* DisplayWidget;
//外部的揭露碰撞
UPROPERTY()
USphereComponent* RevealTrigger;
//内部的交互碰撞
UPROPERTY()
USphereComponent* InteractTrigger;
UFUNCTION()
void OnRevealTriggerIn(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
UFUNCTION()
void OnInteractTriggerIn(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
UFUNCTION()
void OnRevealTriggerOut(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UFUNCTION()
void OnInteractTriggerOut(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
private:
UPROPERTY()
APawn* PlayerPawn;
bool bInRevealRange = false;
bool bInInteractRange = false;
bool bCanInteract = true;
UFUNCTION()
void PerformInitialOverlapCheck();
};
#include "ArknightsFP/Interaction/AFPInteractionInterface.h"
TArray<FName> IAFPInteractionInterface::GetPlayerTokens()
{
return TArray<FName>();
}
// Add default functionality here for any IAFPInteractionInterface functions that are not pure virtual.
void IAFPInteractionInterface::AddInteractable(UActorComponent* Interactable)
{
}
void IAFPInteractionInterface::RemoveInteractable(UActorComponent* Interactable)
{
}
bool IAFPInteractionInterface::bEngageInteract()
{
return false;
}
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "AFPInteractionInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UAFPInteractionInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class ARKNIGHTSFP_API IAFPInteractionInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual TArray<FName> GetPlayerTokens();
virtual void AddInteractable(UActorComponent* Interactable);
virtual void RemoveInteractable(UActorComponent* Interactable);
virtual bool bEngageInteract();
};
#include "ArknightsFP/Interaction/AFPInteractWidget.h"
void UAFPInteractWidget::SetInteractVisibility_Implementation(EInteractState NewVisibility)
{
}
void UAFPInteractWidget::SetBrush_Implementation(FSlateBrush Brush)
{
}
void UAFPInteractWidget::PlayFisrtSight_Implementation()
{
}
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "AFPInteractWidget.generated.h"
UENUM(BlueprintType)
enum class EInteractState : uint8
{
Hidden,
Revealed,
Focused
};
UCLASS()
class ARKNIGHTSFP_API UAFPInteractWidget : public UUserWidget
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent)
void SetBrush(FSlateBrush Brush);
UFUNCTION(BlueprintNativeEvent)
void SetInteractVisibility(EInteractState NewVisibility);
UFUNCTION(BlueprintCallable,BlueprintNativeEvent)
void PlayFisrtSight();
UPROPERTY(BlueprintReadOnly)
bool bUseFirstSight;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment