Initial push to repo
This commit is contained in:
BIN
Plugins/InteractionSystem/Content/Materials/M_UiBackgroundBlur.uasset
LFS
Normal file
BIN
Plugins/InteractionSystem/Content/Materials/M_UiBackgroundBlur.uasset
LFS
Normal file
Binary file not shown.
BIN
Plugins/InteractionSystem/Content/WBP_Interaction.uasset
LFS
Normal file
BIN
Plugins/InteractionSystem/Content/WBP_Interaction.uasset
LFS
Normal file
Binary file not shown.
24
Plugins/InteractionSystem/InteractionSystem.uplugin
Normal file
24
Plugins/InteractionSystem/InteractionSystem.uplugin
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "InteractionSystem",
|
||||
"Description": "",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "InteractionSystem",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Plugins/InteractionSystem/Resources/Icon128.png
LFS
Normal file
BIN
Plugins/InteractionSystem/Resources/Icon128.png
LFS
Normal file
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class InteractionSystem : ModuleRules
|
||||
{
|
||||
public InteractionSystem(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core", "UMG",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"DeveloperSettings"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Components/InteractionWidgetComponent.h"
|
||||
#include "InteractionSettings.h"
|
||||
#include "Interface/InteractableActorInterface.h"
|
||||
#include "Widgets/BaseInteractionWidget.h"
|
||||
|
||||
|
||||
UInteractionWidgetComponent::UInteractionWidgetComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
|
||||
Space = EWidgetSpace::Screen;
|
||||
bManuallyRedraw = true;
|
||||
RedrawTime = 1.f;
|
||||
bDrawAtDesiredSize = true;
|
||||
Pivot = FVector2D(0.5f, 0.5f);
|
||||
TickMode = ETickMode::Disabled;
|
||||
if (const UInteractionSettings* InteractionSettings = GetDefault<UInteractionSettings>())
|
||||
{
|
||||
WidgetClass = InteractionSettings->DefaultInteractionWidgetClass;
|
||||
}
|
||||
}
|
||||
|
||||
void UInteractionWidgetComponent::ToggleWidget(bool bActive)
|
||||
{
|
||||
if (bActive)
|
||||
{
|
||||
if (UBaseInteractionWidget* InteractionWidget = Cast<UBaseInteractionWidget>(GetWidget()))
|
||||
{
|
||||
InteractionWidget->UpdateInfo(IInteractableActorInterface::Execute_GetInteractionText(GetOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
SetVisibility(bActive);
|
||||
RequestRenderUpdate();
|
||||
}
|
||||
|
||||
void UInteractionWidgetComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (UBaseInteractionWidget* InteractionWidget = Cast<UBaseInteractionWidget>(GetWidget()))
|
||||
{
|
||||
InteractionWidget->UpdateInfo(IInteractableActorInterface::Execute_GetInteractionText(GetOwner()));
|
||||
}
|
||||
|
||||
SetPivot(GetPivot() + WidgetOffset);
|
||||
ToggleWidget(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "InteractionSystem.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FInteractionSystemModule"
|
||||
|
||||
void FInteractionSystemModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FInteractionSystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FInteractionSystemModule, InteractionSystem)
|
||||
@@ -0,0 +1,180 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Subsystem/InteractionSubsystem.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "InteractionSettings.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "Interface/InteractableActorInterface.h"
|
||||
|
||||
void UInteractionSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
if (GetWorld())
|
||||
{
|
||||
GetWorld()->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &UInteractionSubsystem::RegisterInteractableActor));
|
||||
GetWorld()->AddOnActorDestroyedHandler(FOnActorDestroyed::FDelegate::CreateUObject(this, &UInteractionSubsystem::UnregisterInteractableActor));
|
||||
}
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::Deinitialize()
|
||||
{
|
||||
InteractableActorList.Empty();
|
||||
|
||||
if (UInteractionSubsystem* IS = GetWorld()->GetSubsystem<UInteractionSubsystem>())
|
||||
{
|
||||
if (IS->CurrentlyInteractableObject.IsValid())
|
||||
{
|
||||
IInteractableActorInterface::Execute_ToggleInteractableUI(IS->CurrentlyInteractableObject.Get(), false);
|
||||
}
|
||||
IS->CurrentlyInteractableObject = nullptr;
|
||||
}
|
||||
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
bool UInteractionSubsystem::ShouldCreateSubsystem(UObject* Outer) const
|
||||
{
|
||||
if (!Super::ShouldCreateSubsystem(Outer)) return false;
|
||||
|
||||
if (!Outer || !Outer->GetWorld()) return false;
|
||||
|
||||
UWorld* World = Outer->GetWorld();
|
||||
return World->WorldType == EWorldType::Type::Game || World->WorldType == EWorldType::Type::PIE;
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::OnWorldBeginPlay(UWorld& InWorld)
|
||||
{
|
||||
Super::OnWorldBeginPlay(InWorld);
|
||||
|
||||
RescanWorld();
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::Tick(float DeltaTime)
|
||||
{
|
||||
GetClosestInteractable(this);
|
||||
}
|
||||
|
||||
AActor* UInteractionSubsystem::GetClosestInteractable(const UObject* WorldContext)
|
||||
{
|
||||
AActor* Closest = nullptr;
|
||||
|
||||
if (!IsValid(WorldContext) || !IsValid(WorldContext->GetWorld()))
|
||||
{
|
||||
return Closest;
|
||||
}
|
||||
|
||||
UInteractionSubsystem* IS = WorldContext->GetWorld()->GetSubsystem<UInteractionSubsystem>();
|
||||
ensure(IS);
|
||||
|
||||
const APlayerController* PC = WorldContext->GetWorld()->GetFirstPlayerController();
|
||||
if (!PC) return Closest;
|
||||
|
||||
const ACharacter* PlayerCharacter = Cast<ACharacter>(PC->GetPawn());
|
||||
if (!PlayerCharacter) return Closest;
|
||||
|
||||
const UInteractionSettings* InteractionSettings = GetDefault<UInteractionSettings>();
|
||||
ensure(InteractionSettings);
|
||||
|
||||
float ClosestDist = 9999999.f;
|
||||
for (TWeakObjectPtr<AActor> Actor : IS->InteractableActorList)
|
||||
{
|
||||
if (!Actor.IsValid())
|
||||
continue;
|
||||
|
||||
const float Dist = FVector::DistSquared(Actor->GetActorLocation(), PlayerCharacter->GetActorLocation());
|
||||
const float MaxInteractionDistance = IInteractableActorInterface::Execute_GetInteractionDistance(Actor.Get());
|
||||
|
||||
if (FMath::Pow(MaxInteractionDistance, 2) < Dist || Dist > ClosestDist)
|
||||
continue;
|
||||
|
||||
FVector2D ScreenLoc;
|
||||
if (!PC->ProjectWorldLocationToScreen(Actor->GetActorLocation(), ScreenLoc))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IInteractableActorInterface::Execute_IsInteractable(Actor.Get()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClosestDist = Dist;
|
||||
Closest = Actor.Get();
|
||||
}
|
||||
|
||||
if (IS->CurrentlyInteractableObject != Closest)
|
||||
{
|
||||
if (IS->CurrentlyInteractableObject.IsValid())
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("Turning off last interactable: %s"), *IS->CurrentlyInteractableObject.Get()->GetActorNameOrLabel())
|
||||
IInteractableActorInterface::Execute_ToggleInteractableUI(IS->CurrentlyInteractableObject.Get(), false);
|
||||
}
|
||||
if (Closest)
|
||||
{
|
||||
// UE_LOG(LogTemp, Warning, TEXT("Turning ON interactable: %s"), *Closest->GetActorNameOrLabel())
|
||||
IInteractableActorInterface::Execute_ToggleInteractableUI(Closest, true);
|
||||
}
|
||||
IS->CurrentlyInteractableObject = Closest;
|
||||
}
|
||||
|
||||
return Closest;
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::UpdateInteractableText(AActor* Interactable)
|
||||
{
|
||||
if (!Interactable) return;
|
||||
|
||||
UInteractionSubsystem* InteractionSubsystem = Interactable->GetWorld()->GetSubsystem<UInteractionSubsystem>();
|
||||
if (!InteractionSubsystem) return;
|
||||
|
||||
if (InteractionSubsystem->CurrentlyInteractableObject != Interactable) return;
|
||||
|
||||
IInteractableActorInterface::Execute_ToggleInteractableUI(InteractionSubsystem->CurrentlyInteractableObject.Get(), true);
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::RescanWorld()
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
int32 Count = 0;
|
||||
|
||||
if (!World)
|
||||
return;
|
||||
|
||||
// Prune stuff
|
||||
InteractableActorList.RemoveAllSwap(
|
||||
[](TWeakObjectPtr<AActor> Actor)
|
||||
{
|
||||
return !Actor.IsValid();
|
||||
}
|
||||
);
|
||||
|
||||
for (TActorIterator<AActor> It(World); It; ++It)
|
||||
{
|
||||
Count++;
|
||||
AActor* Actor = *It;
|
||||
if (IsValid(Actor) && Actor->Implements<UInteractableActorInterface>())
|
||||
{
|
||||
RegisterInteractableActor(Actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::RegisterInteractableActor(AActor* InteractableActor)
|
||||
{
|
||||
if (IsValid(InteractableActor) && InteractableActor->Implements<UInteractableActorInterface>())
|
||||
{
|
||||
InteractableActorList.Add(InteractableActor);
|
||||
}
|
||||
}
|
||||
|
||||
void UInteractionSubsystem::UnregisterInteractableActor(AActor* InteractableActor)
|
||||
{
|
||||
InteractableActorList.RemoveAllSwap(
|
||||
[InteractableActor](TWeakObjectPtr<AActor> Actor)
|
||||
{
|
||||
return !Actor.IsValid() || Actor.Get() == InteractableActor;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Widgets/BaseInteractionWidget.h"
|
||||
@@ -0,0 +1,28 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "InteractionWidgetComponent.generated.h"
|
||||
|
||||
|
||||
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
||||
class INTERACTIONSYSTEM_API UInteractionWidgetComponent : public UWidgetComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInteractionWidgetComponent();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Interaction Widget Component")
|
||||
void ToggleWidget(bool bActive);
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Interaction Widget Component")
|
||||
FVector2D WidgetOffset;
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DeveloperSettings.h"
|
||||
#include "InteractionSettings.generated.h"
|
||||
|
||||
class UBaseInteractionWidget;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Config=Game, DefaultConfig, meta=(DisplayName="Interaction System"))
|
||||
class INTERACTIONSYSTEM_API UInteractionSettings : public UDeveloperSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// Max distance at which interactable will be considered active
|
||||
UPROPERTY(Config, EditAnywhere)
|
||||
float InteractableMaxDistance = 1000.f;
|
||||
|
||||
// Angle from the camera view towards object to consider "in view"
|
||||
UPROPERTY(Config, EditAnywhere)
|
||||
float InteractableCameraViewAngle = 60.f;
|
||||
|
||||
UPROPERTY(Config, EditAnywhere)
|
||||
TSubclassOf<UBaseInteractionWidget> DefaultInteractionWidgetClass;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FInteractionSystemModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "InteractionSettings.h"
|
||||
#include "InteractableActorInterface.generated.h"
|
||||
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UInteractableActorInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class INTERACTIONSYSTEM_API IInteractableActorInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
|
||||
FText GetInteractionText() const;
|
||||
virtual FText GetInteractionText_Implementation() const { return FText::GetEmpty(); }
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
|
||||
bool IsInteractable() const;
|
||||
virtual bool IsInteractable_Implementation() const { return false; }
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
|
||||
void Interact(AActor* InteractionInstigator);
|
||||
virtual void Interact_Implementation(AActor* InteractionInstigator) {}
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
|
||||
void ToggleInteractableUI(bool bActive);
|
||||
virtual void ToggleInteractableUI_Implementation(bool bActive) {}
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interactable")
|
||||
float GetInteractionDistance() const;
|
||||
virtual float GetInteractionDistance_Implementation() const
|
||||
{
|
||||
return GetDefault<UInteractionSettings>()->InteractableMaxDistance;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "Tickable.h"
|
||||
#include "Stats/Stats.h"
|
||||
|
||||
#include "InteractionSubsystem.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class INTERACTIONSYSTEM_API UInteractionSubsystem : public UWorldSubsystem, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
|
||||
virtual bool IsTickable() const override { return !IsTemplate(); }
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UInteractionSubsystem, STATGROUP_Tickables); }
|
||||
|
||||
static AActor* GetClosestInteractable(const UObject* WorldContext);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Interactable")
|
||||
static void UpdateInteractableText(AActor* Interactable);
|
||||
|
||||
protected:
|
||||
|
||||
UFUNCTION()
|
||||
void RescanWorld();
|
||||
|
||||
void RegisterInteractableActor(AActor* InteractableActor);
|
||||
void UnregisterInteractableActor(AActor* InteractableActor);
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TWeakObjectPtr<AActor>> InteractableActorList;
|
||||
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<AActor> CurrentlyInteractableObject;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "BaseInteractionWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class INTERACTIONSYSTEM_API UBaseInteractionWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "Interaction System")
|
||||
void UpdateInfo(const FText& InteractionText);
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user