My Ruins_Escape take

Hey everyone!

Here is my variant of Building Escape section. The assets have been taken from Modular Lost Ruin Kit at EGS Store for free. I have added custom door though in order to open in downwards (change Z location i.o. rotation, see my cpp files for more details). Also, I have locked the rotation of grabbed object.

OpenDoor.h

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Engine/TriggerVolume.h"
#include "OpenDoor.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DUNGEONESCAPE_API UOpenDoor : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UOpenDoor();

	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	void LowerTheGate(float DeltaTime);
	void UpperTheGate(float DeltaTime);
	float TotalMassOfActors() const;
	void FindAudioComponent();
	void FindPressurePlate();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;
	

private:
	bool bOpenSoundWasPlayed = false;
	bool bCloseSoundWasPlayed = true;
	
	float DoorInitial_Z;
	float DoorCurrent_Z;
	float DoorTarget_Z = -170.f;
	
	UPROPERTY(EditAnywhere)
	float OpeningSpeed = 30.f;
	
	float DoorLastOpened = 0.f;

	UPROPERTY(EditAnywhere)
	float DoorCloseDelay = 1.f;

	UPROPERTY(EditAnywhere)
	float DoorCloseSpeed = 2.5f;

	UPROPERTY(EditAnywhere)
	float RequiredMassToOpenDoor = 20.f;

	UPROPERTY()
	UAudioComponent* AudioComponent = nullptr;
	UPROPERTY(EditAnywhere)
	ATriggerVolume* PressurePlate = nullptr;

};

OpenDoor.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenDoor.h"
#include "Components/AudioComponent.h"
#include "GameFramework/Actor.h"
#include "Components/PrimitiveComponent.h"

#define OUT

// Sets default values for this component's properties
UOpenDoor::UOpenDoor()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;
}

// Called when the game starts
void UOpenDoor::BeginPlay()
{
	Super::BeginPlay();

	DoorInitial_Z = GetOwner()->GetActorLocation().Z;
	DoorCurrent_Z = DoorInitial_Z;

	FindPressurePlate();
	FindAudioComponent();
}

void UOpenDoor::FindPressurePlate()
{
	if (!PressurePlate)
	{
		UE_LOG(LogTemp, Error, TEXT("%s has OpenDoor component, but no PressurePlate set!!!"), *GetOwner()->GetName());
	}
}
void UOpenDoor::FindAudioComponent()
{
	AudioComponent = GetOwner()->FindComponentByClass<UAudioComponent>();

	if (!AudioComponent)
	{
		UE_LOG(LogTemp, Error, TEXT("No AudioComponent is missing in %s"), *GetOwner()->GetName());
	}
}

// Called every frame
void UOpenDoor::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (TotalMassOfActors() > RequiredMassToOpenDoor)
	{
		LowerTheGate(DeltaTime);
		DoorLastOpened = GetWorld()->GetTimeSeconds();
	}
	else
	{
		if (GetWorld()->GetTimeSeconds() >= DoorLastOpened + DoorCloseDelay) UpperTheGate(DeltaTime);
	}
}

void UOpenDoor::LowerTheGate(float DeltaTime)
{
	DoorCurrent_Z = FMath::FInterpConstantTo(DoorCurrent_Z, DoorTarget_Z, DeltaTime, OpeningSpeed);
	FVector DoorLocation = GetOwner()->GetActorLocation();
	DoorLocation.Z = DoorCurrent_Z;
	GetOwner()->SetActorLocation(DoorLocation);

	if (!AudioComponent) return;

	if (bOpenSoundWasPlayed == false)
	{
		UE_LOG(LogTemp, Warning, TEXT("Lower The Gate!"));
		AudioComponent->Play(); // open the gate
		bOpenSoundWasPlayed = true;
	}
}

void UOpenDoor::UpperTheGate(float DeltaTime)
{
	DoorCurrent_Z = FMath::FInterpConstantTo(DoorCurrent_Z, DoorInitial_Z, DeltaTime, OpeningSpeed);
	FVector DoorLocation = GetOwner()->GetActorLocation();
	DoorLocation.Z = DoorCurrent_Z;
	GetOwner()->SetActorLocation(DoorLocation);
	
	if (!AudioComponent) return;
	
	if (bCloseSoundWasPlayed == false)
	{
		UE_LOG(LogTemp, Warning, TEXT("Upper The Gate!"));
		AudioComponent->Play(); // close the gate
		bCloseSoundWasPlayed = true;
		bOpenSoundWasPlayed = false;
	}
}

float UOpenDoor::TotalMassOfActors() const
{
	float TotalMassOfActor = 0.f;

	//Find all overlapping actors
	TArray<AActor*> OverlappingActors;
	if (!PressurePlate) // protect against nullptr
	{
		return TotalMassOfActor;
	}
	PressurePlate->GetOverlappingActors(OUT OverlappingActors);

	//add up their masses
	for (AActor* Actor : OverlappingActors)
	{
		TotalMassOfActor += Actor->FindComponentByClass<UPrimitiveComponent>()->GetMass();
	}
	return TotalMassOfActor;
}

Grabber.h

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

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PhysicsEngine/PhysicsHandleComponent.h"
#include "Grabber.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DUNGEONESCAPE_API UGrabber : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UGrabber();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	float Reach = 200.f;
	
	UPROPERTY()
	UPhysicsHandleComponent* PhysicsHandle = nullptr;
	UPROPERTY()
	UInputComponent* InputComponent = nullptr;

	void FindPhysicsHandle();
	void SetUpInputComponent();
	void Grab();
	void Release();

	// Return the first Actor within reach with PhysicsBody
	FHitResult GetFirstPhysicsBodyInReach() const;
	// return the vector's end of our line trace
	FVector GetPlayersReach() const;
	// return player's position in the world
	FVector GetPlayersWorldPos() const;
	
};

Grabber.cpp

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


#include "Grabber.h"

#define OUT

// Sets default values for this component's properties
UGrabber::UGrabber()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UGrabber::BeginPlay()
{
	Super::BeginPlay();
	FindPhysicsHandle();
	SetUpInputComponent();
}


// Called every frame
void UGrabber::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	//if physics handle is attached (if we actually grabbed something)
	if (!PhysicsHandle) // protect against nullptr
	{
		return;
	}
	if (PhysicsHandle->GrabbedComponent)
	{
		//move the object we are holding
		PhysicsHandle->SetTargetLocation(GetPlayersReach());
	}
}

void UGrabber::FindPhysicsHandle()
{
	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();
	if (!PhysicsHandle)
	{
		UE_LOG(LogTemp, Error, TEXT("The component PhysicsHandle in class %s was NOT found!"), *GetOwner()->GetName());
	}
}

void UGrabber::SetUpInputComponent()
{
	InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();

	if (InputComponent)
	{
		InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);
		InputComponent->BindAction("Grab", IE_Released, this, &UGrabber::Release);
	}
	else UE_LOG(LogTemp, Error, TEXT("The component InputComponent in class %s was NOT found!"), *GetOwner()->GetName());
}

void UGrabber::Grab()
{
	// only ray cast when key is pressed and see if we reach any actor with a Physics Body Collision Channel set.
	FHitResult HitResult = GetFirstPhysicsBodyInReach();
	UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();

	// + lock component from rotating when grabbed
	const FRotator ZeroRotation = {0,0,0};
	
	//if we hit something, then attach the physics handle
	AActor* ActorHit = HitResult.GetActor();
	if (ActorHit)
	{
		if (!PhysicsHandle) // protect against nullptr
		{
			return;
		}
		PhysicsHandle->GrabComponentAtLocationWithRotation(
		ComponentToGrab,
		NAME_None,
		GetPlayersReach(),
		ZeroRotation
	);
	}	
}

void UGrabber::Release()
{
	if (!PhysicsHandle) // protect against nullptr
	{
		return;
	}
	PhysicsHandle->ReleaseComponent();
}

FHitResult UGrabber::GetFirstPhysicsBodyInReach() const
{
	// ray-cast out to a certain distance (reach)
	FHitResult Hit;
	FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());
	GetWorld()->LineTraceSingleByObjectType(
		OUT Hit,
		GetPlayersWorldPos(),
		GetPlayersReach(),
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
		TraceParams
	);
	return Hit;
}

FVector UGrabber::GetPlayersReach() const
{
	FVector PlayerViewPoint_Location;
	FRotator PlayerViewPoint_Rotation;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerViewPoint_Location,
		OUT PlayerViewPoint_Rotation
	);
	return PlayerViewPoint_Location + PlayerViewPoint_Rotation.Vector() * Reach;;
}

FVector UGrabber::GetPlayersWorldPos() const
{
	FVector PlayerViewPoint_Location;
	FRotator PlayerViewPoint_Rotation;
	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint(
		OUT PlayerViewPoint_Location,
		OUT PlayerViewPoint_Rotation
	);

	return PlayerViewPoint_Location;
}


3 Likes

Privacy & Terms