Feedback #1 update #10

Merged
oleg.petruny merged 2 commits from feedback1 into master 2025-01-16 12:40:00 +00:00
19 changed files with 211 additions and 59 deletions

View File

@ -19,11 +19,11 @@ Lost_Edge/ \
# Building your own project copy
Building a copy of the game requires only [UnrealProject/LostEdge/](UnrealProject/Lost_Edge) directory. \
For that purposes you can download the directory as archive or do a sparse checkout via commands below.
- git clone --no-checkout https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git
- cd Lost_Edge/
- git sparse-checkout init
- git sparse-checkout set UnrealProject/Lost_Edge
- git checkout master
- `git clone --no-checkout https://pixelyfier.com/git/Pixelyfier/Lost_Edge.git`
- `cd Lost_Edge/`
- `git sparse-checkout init`
- `git sparse-checkout set UnrealProject/Lost_Edge`
- `git checkout master`
# Git lfs common issues
The download can be sometimes too long which makes git lfs drop the connection with error \

View File

@ -122,26 +122,31 @@ ManualIPAddress=
-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision")
+Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap),(Channel="InteractableTrace",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.")
+Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ")
+Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ")
+Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.")
+Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.")
+Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ")
+Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="InteractableTrace")),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ")
+Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.")
+Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.")
+Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors")
+Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors")
+Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.")
+Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.")
+Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.")
+Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.")
+Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.")
+Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.")
+Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="Interactable",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Interactable")),HelpMessage="WorldDynamic objects derived from AInteractable.")
+Profiles=(Name="AgeOfWarUnitPlayer",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Vehicle",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Interactable")),HelpMessage="Minigame object")
+Profiles=(Name="AgeOfWarUnitComputer",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Destructible",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore),(Channel="Interactable")),HelpMessage="Minigame object")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="Interactable")
+Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
+Profiles=(Name="Interactable",CollisionEnabled=QueryAndPhysics,bCanModify=True,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="InteractableTrace")),HelpMessage="WorldDynamic objects derived from AInteractable.")
+Profiles=(Name="AgeOfWarUnitPlayer",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Vehicle",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="InteractableTrace")),HelpMessage="Minigame object")
+Profiles=(Name="AgeOfWarUnitComputer",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="Destructible",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore),(Channel="InteractableTrace")),HelpMessage="Minigame object")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="InteractableTrace")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Interactable")
+EditProfiles=(Name="Pawn",CustomResponses=((Channel="InteractableTrace"),(Channel="Interactable",Response=ECR_Overlap)))
+EditProfiles=(Name="OverlapAllDynamic",CustomResponses=((Channel="InteractableTrace",Response=ECR_Overlap),(Channel="Interactable",Response=ECR_Overlap)))
+EditProfiles=(Name="CharacterMesh",CustomResponses=((Channel="Interactable",Response=ECR_Ignore)))
+EditProfiles=(Name="Spectator",CustomResponses=((Channel="Interactable",Response=ECR_Ignore)))
-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
@ -163,6 +168,7 @@ ManualIPAddress=
+CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
+CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
+CollisionChannelRedirects=(OldName="Interactable",NewName="InteractableTrace")
[/Script/FMODStudio.FMODSettings]
bLoadAllBanks=True

View File

@ -1,5 +1,7 @@
# How to compile
It's recommended to work in Unreal Engine 5.4 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file "UnrealProject/Lost_Edge/Lost_Edge.uproject". Or by typing "%UE5PATH%\Engine\Build\BatchFiles\GenerateProjectFiles.bat" "%REPOPATH%\UnrealProject\Lost_Edge\Lost_Edge.uproject". \
After generating, "Lost_Edge.sln" can be opened and project can start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own "check()" function. This error can be used to determine that in the file "%UE5PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp" it is necessary to comment out the warning macro on lines 52-54 and rename the function itself on line 957 to, for example, "checkcv()".
After this modification, the build should run fine and it is possible to open the project in UnrealEngine.
It's recommended to work in Unreal Engine 5.5 and to install Visual Studio with the necessary packages as it described in the [official documentation](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine) (recommended settings aren't needed). \
After repo cloning, the Visual Studio project files needs to be generated. It can be done in the explorer RMB context menu of file `UnrealProject/Lost_Edge/Lost_Edge.uproject` - option `Generate project files`. Or by running `%UE5_PATH%\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe -game "%REPO_PATH%\Lost_Edge\UnrealProject\Lost_Edge\Lost_Edge.uproject" -VSCode`. \
After generating, appears the `Lost_Edge.sln`. Opent it and start building for the first time, which will fail. This is because of using OpenCV plugin that together with UnrealEngine implement their own `check()` function. The error can be used to determine that in the file `%UE5_PATH%\Engine\Plugins\Runtime\OpenCV\Source\ThirdParty\OpenCV\include\opencv2\core\utility.hpp` it is necessary to:
- comment out the warning macro on lines 52-54,
- and rename the function itself on line 957 to, for example, `checkcv()`.
After opencv library modification, the build should run fine and it is possible to open the `Lost_Edge.uproject` in UnrealEngine.

View File

@ -24,6 +24,7 @@ UInCameraInteractableActivator::UInCameraInteractableActivator(const FObjectInit
capturer->interactableInScreenDelegate.BindUObject(this, &UInCameraInteractableActivator::NewSeenInteractable);
capturer->SetupAttachment(this);
capturer->scanDistance = scanDistance;
capturer->ignoredActors.Add(actor);
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;

View File

@ -18,8 +18,9 @@ UInteractableActivator::UInteractableActivator(const FObjectInitializer& ObjectI
if(HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
return;
actor = GetOwner();
world = GetWorld();
player = Cast<APlayerBase>(GetOwner());
player = APlayerBase::Get();
collisionChannel = ECC_GameTraceChannel1;
if(auto collisions = UCollisionProfile::Get())

View File

@ -46,6 +46,7 @@ protected:
UPROPERTY(EditDefaultsOnly)
float scanDistance = 250;
class AActor* actor;
class UWorld* world;
class APlayerBase* player;
ECollisionChannel collisionChannel;

View File

@ -248,6 +248,8 @@ void UInteractableScreenCapturer::Process()
FHitResult result{};
FVector startLocation = view.Location;
FVector endLocation = worldLoc + worldDir * scanDistance;
FCollisionQueryParams hitParams;
hitParams.AddIgnoredActors(ignoredActors);
world->LineTraceSingleByChannel(
result,
startLocation,

View File

@ -28,6 +28,8 @@ public:
UPROPERTY(EditAnywhere)
float scanDistance = 7000;
TArray<AActor*> ignoredActors;
protected:
/** Enques render thread task to find obect on screen */
void Process();

View File

@ -21,12 +21,15 @@ void URaycastInteractableActivator::Scan_Implementation()
FHitResult result;
FVector startLocation = GetComponentLocation();
FVector endLocation = startLocation + (GetComponentRotation().Vector() * scanDistance);
FCollisionQueryParams hitParams;
hitParams.AddIgnoredActor(actor);
world->LineTraceSingleByChannel(
result,
startLocation,
endLocation,
collisionChannel
collisionChannel,
hitParams
);
if(result.bBlockingHit)

View File

@ -2,6 +2,7 @@
#include "Interactable.h"
#include "Engine/CollisionProfile.h"
#include "Kismet/GameplayStatics.h"
#include "Activators/InteractableActivator.h"
@ -11,6 +12,15 @@
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
#include <optional>
namespace
{
constexpr auto collisionProfile = TEXT("Interactable");
constexpr int32 sawStencil = 128;
constexpr int32 outlineStencil = 132;
}
TSet<TSubclassOf<class UInteractableActivator>> AInteractable::interactionActivators = {};
TSet<TSubclassOf<class UInteractableModificator>> AInteractable::interactionModificators = {};
@ -33,6 +43,19 @@ void AInteractable::AppendModificatorClass(TSubclassOf<class UInteractableModifi
ACustomPlayerController::AppendInputContext(IC);
}
ECollisionChannel AInteractable::GetCollisionChannel()
{
static std::optional<FCollisionResponseTemplate> collisionTemplate;
if(!collisionTemplate.has_value())
{
FCollisionResponseTemplate buffer;
UCollisionProfile::Get()->GetProfileTemplate(collisionProfile, buffer);
collisionTemplate = buffer;
}
return collisionTemplate->ObjectType;
}
int32 AInteractable::GetActivatedFlags()
{
return activated;
@ -83,12 +106,12 @@ void AInteractable::BeginPlay()
{
if(activatorTypes)
{
collision->SetCollisionProfileName(TEXT("Interactable"));
collision->SetCollisionProfileName(collisionProfile);
}
if(activatorTypes & static_cast<uint8>(EActivatorType::Saw))
{
collision->CustomDepthStencilValue = 128;
collision->CustomDepthStencilValue = sawStencil;
collision->CustomDepthStencilWriteMask = ERendererStencilMask::ERSM_Default;
collision->SetRenderCustomDepth(true);
}
@ -144,7 +167,7 @@ void AInteractable::Activate(EActivatorType type)
if(activated & static_cast<uint8>(EActivatorType::Use))
{
for(auto collision : collisions)
collision->SetCustomDepthStencilValue(132);
collision->SetCustomDepthStencilValue(outlineStencil);
}
OnActivate(type);
@ -178,8 +201,12 @@ void AInteractable::Deactivate(EActivatorType type)
if(!(activated & static_cast<uint8>(EActivatorType::Use)))
{
for(auto collision : collisions)
collision->SetCustomDepthStencilValue(0);
if(modificators.Contains(EActivatorType::Saw))
for(auto collision : collisions)
collision->SetCustomDepthStencilValue(sawStencil);
else
for(auto collision : collisions)
collision->SetCustomDepthStencilValue(0);
}
OnDeactivate(type);

View File

@ -43,6 +43,8 @@ public:
static void AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator);
static void AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator);
static ECollisionChannel GetCollisionChannel();
/** Interactables shared cache */
static TSet<TSubclassOf<class UInteractableActivator>> interactionActivators;
static TSet<TSubclassOf<class UInteractableModificator>> interactionModificators;

View File

@ -2,8 +2,12 @@
#include "MoveInteractableModificator.h"
#include "Components/BoxComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/PrimitiveComponent.h"
#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Interactable/Interactable.h"
#include "MainGameModeBase.h"
@ -20,6 +24,11 @@ UMoveInteractableModificator::UMoveInteractableModificator(const FObjectInitiali
inputMapping = asset.Object;
actor = Cast<AInteractable>(GetOwner());
if(actor)
{
shape = FCollisionShape::MakeBox(actor->GetComponentsBoundingBox().GetExtent());
primitive = actor->GetComponentByClass<UPrimitiveComponent>();
}
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
@ -70,7 +79,7 @@ void UMoveInteractableModificator::Bind_Implementation(class UEnhancedInputCompo
}
lastInput = input;
player = Cast<APlayerBase>(Cast<APlayerController>(lastInput->GetOwner())->GetPawn());
player = APlayerBase::Get();
SetComponentTickEnabled(true);
OnMoveActivated.Broadcast();
@ -98,6 +107,16 @@ void UMoveInteractableModificator::TurnOnHolding()
{
FScopeLock lock1(&critical);
holding = true;
if(primitive && primitive->IsSimulatingPhysics())
{
positionUpdateType = PositionUpdateType::Physics;
player->physicsHandle->GrabComponentAtLocationWithRotation(primitive, TEXT("None"), actor->GetActorLocation(), actor->GetActorRotation());
}
else
{
positionUpdateType = PositionUpdateType::Simple;
}
ProcessState();
distance = (player->GetCameraLocation() - actor->GetActorLocation()).Length();
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::Hold);
@ -107,6 +126,8 @@ void UMoveInteractableModificator::TurnOffHolding()
{
FScopeLock lock1(&critical);
holding = false;
if(positionUpdateType == PositionUpdateType::Physics)
player->physicsHandle->ReleaseComponent();
ProcessState();
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::Unhold);
}
@ -139,6 +160,11 @@ void UMoveInteractableModificator::ProcessState()
else
{
actor->activationLockers.Remove(this);
if(indicating)
{
indicating = false;
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::TurnOffIndication);
}
if((actor->GetActivatedFlags() & static_cast<int32>(GetActivatorTypes())) == 0
&& !bindindingHandlers.IsEmpty())
{
@ -152,27 +178,42 @@ void UMoveInteractableModificator::UpdatePosition()
if(!player || (!holding && !rotating))
return;
if(player->IsOverlappingActor(actor))
return;
auto camLoc = player->GetCameraLocation();
auto dir = player->GetCameraDirection();
auto newLoc = camLoc + dir * distance;
auto oldLoc = actor->GetActorLocation();
//auto interpLoc = FMath::VInterpTo(oldLoc, newLoc, GetWorld()->GetDeltaSeconds(), 20.0f);
//auto interpLoc = FMath::Lerp(oldLoc, newLoc, 0.5f);
FHitResult hit;
actor->SetActorLocation(newLoc, true, &hit, ETeleportType::None);
static bool indicating = false;
if(hit.bBlockingHit && !indicating)
auto rot = actor->GetActorQuat();
bool blocked = MoveIsBlocked(newLoc, rot) || player->nearScanner->ComponentOverlapComponent(primitive, newLoc, actor->GetActorQuat(), {});
if(blocked)
{
indicating = true;
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::TurnOnIndication);
if(!indicating)
{
indicating = true;
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::TurnOnIndication);
}
return;
}
else if(!hit.bBlockingHit && indicating)
else
{
indicating = false;
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::TurnOffIndication);
if(indicating)
{
indicating = false;
AMainGameModeBase::GetWidgetsManager()->AnimateInteractionHint(this, 0, EInputAnimatedWidgetAnimation::TurnOffIndication);
}
}
switch(positionUpdateType)
{
case PositionUpdateType::Physics:
player->physicsHandle->SetTargetLocation(newLoc);
break;
default:
actor->SetActorLocation(newLoc, true, nullptr, ETeleportType::None);
break;
}
}
void UMoveInteractableModificator::Zooming(const FInputActionValue& value)
@ -180,8 +221,14 @@ void UMoveInteractableModificator::Zooming(const FInputActionValue& value)
if(!holding && !rotating)
return;
distance += value.Get<float>() * zoomSpeed;
distance = FMath::Clamp<float>(distance, minDistance, maxDistance);
float delta = value.Get<float>() * zoomSpeed;
auto start = actor->GetActorLocation();
auto end = start + player->GetCameraDirection() * delta;
auto rot = actor->GetActorQuat();
if(MoveIsBlocked(end, rot))
return;
distance = FMath::Clamp<float>(distance + delta, minDistance, maxDistance);
}
void UMoveInteractableModificator::Rotating(const FInputActionValue& axis)
@ -190,9 +237,35 @@ void UMoveInteractableModificator::Rotating(const FInputActionValue& axis)
return;
auto v = axis.Get<FVector>();
auto vec = player->GetCameraDirection();
auto currentRot = actor->GetActorRotation();
auto currentLoc = actor->GetActorLocation();
auto newRot = FRotator{ v.Y * vec.X, v.X, v.Y * vec.Y * -1 };
actor->AddActorWorldRotation(newRot, true, nullptr, ETeleportType::None); // sweep not supported from 4.xx+
}
auto dir = player->GetCameraDirection();
auto addRot = FRotator{ v.Y * dir.X, v.X, v.Y * dir.Y * -1 };
auto newRot = (addRot.Quaternion() * actor->GetActorQuat()).Rotator();
// rotation sweep is unsupported for some reason
// if(MoveIsBlocked(actor->GetActorLocation(), newRot.Quaternion()))
// return;
if(player->nearScanner->ComponentOverlapComponent(primitive, actor->GetActorLocation(), newRot, {})
|| player->feetScanner->ComponentOverlapComponent(primitive, actor->GetActorLocation(), newRot, {}))
return;
switch(positionUpdateType)
{
case PositionUpdateType::Physics:
player->physicsHandle->SetTargetRotation(newRot);
break;
default:
actor->SetActorRotation(newRot, ETeleportType::None);
break;
}
}
bool UMoveInteractableModificator::MoveIsBlocked(const FVector& end, const FQuat& rot)
{
FHitResult hit;
FVector start = actor->GetActorLocation();
FCollisionQueryParams hitParams;
hitParams.AddIgnoredActor(actor);
actor->GetWorld()->SweepSingleByChannel(hit, start, end, rot, AInteractable::GetCollisionChannel(), shape, hitParams);
return hit.bBlockingHit;
}

View File

@ -53,6 +53,7 @@ private:
void UpdatePosition();
void Zooming(const FInputActionValue& value);
void Rotating(const FInputActionValue& axis);
bool MoveIsBlocked(const FVector& end, const FQuat& rot);
class UEnhancedInputComponent* lastInput = nullptr;
TArray<int32> bindindingHandlers;
@ -61,4 +62,13 @@ private:
class AInteractable* actor = nullptr;
class APlayerBase* player = nullptr;
FCriticalSection critical;
bool indicating = false;
FCollisionShape shape;
class UPrimitiveComponent* primitive;
enum PositionUpdateType : uint8
{
Simple,
Physics
} positionUpdateType = Simple;
};

View File

@ -3,11 +3,14 @@
#include "PlayerBase.h"
#include "Camera/CameraComponent.h"
#include "Components/BoxComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "CustomGameSettings.h"
#include "CustomPlayerController.h"
@ -40,6 +43,12 @@ APlayerBase::APlayerBase()
itemDropSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("ItemDropSpringArm"));
itemDropSpringArm->TargetArmLength = -200;
itemDropSpringArm->SetupAttachment(camera);
physicsHandle = CreateDefaultSubobject<UPhysicsHandleComponent>(TEXT("PhysicsHandle"));
nearScanner = CreateDefaultSubobject<UCapsuleComponent>(TEXT("NearScanner"));
nearScanner->SetupAttachment(RootComponent);
feetScanner = CreateDefaultSubobject<UBoxComponent>(TEXT("FeetScanner"));
feetScanner->SetupAttachment(RootComponent);
}
void APlayerBase::Tick(float DeltaTime)
@ -195,6 +204,10 @@ void APlayerBase::Jump()
if(jumpLocked)
return;
if(auto primitive = physicsHandle->GetGrabbedComponent())
if(feetScanner->ComponentOverlapComponent(primitive, primitive->GetComponentLocation(), primitive->GetComponentRotation(), {}))
return;
ACharacter::Jump();
}

View File

@ -80,9 +80,18 @@ public:
FVector moveVector;
UPROPERTY()
class AActor* leftPocketItem = nullptr;
UPROPERTY()
class AActor* rightPocketItem = nullptr;
UPROPERTY()
class UPhysicsHandleComponent* physicsHandle;
UPROPERTY(EditAnywhere)
class UCapsuleComponent* nearScanner;
UPROPERTY(EditAnywhere)
class UBoxComponent* feetScanner;
protected:
virtual void BeginPlay() override;

View File

@ -8,8 +8,8 @@ Edit voice name in `GenerateDialogue.py` script.
# System requirements
OS: Windows 10 or 11 \
GPU: NVIDIA \
Storage: ~15GB
Storage: ~15GB \
It's preffered to have NVIDIA CUDA GPU. (Render times: 1070ti - 3min, i7700k - 18min )
# Install.ps1
By executing the PowerShell script `Install.ps1` the following packages are installed:
@ -19,7 +19,7 @@ By executing the PowerShell script `Install.ps1` the following packages are inst
- [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) 12.4.1
- [PyTorch CUDA](https://pytorch.org/get-started/locally/) 12.4
- [Coqui TTS](https://github.com/idiap/coqui-ai-TTS) 0.25.1
- [Suno-ai/bark](https://github.com/suno-ai/bark)
- [Suno-ai/bark](https://github.com/suno-ai/bark) \
It's recommended to run installation script as administrator and restart machine after install completion.
# Uninstall.ps1
@ -28,5 +28,5 @@ The following packages/data aren't removed:
- Python
- Visual Studio Installer
- voices/
- _Something will of course left_
- _Something will of course left_ \
It's recommended to run uninstallat script as administrator and restart machine after uninstall completion.