Building Escape Course (COMPLETE)

Just made it to “Getting an Actor’s Transform,” and here is how I wrote the challenge code:


image

Of course, I took the obvious hints, but even though it seems I took the long way here, I was thrilled to see this code work. That was my proudest coding moment yet. :slight_smile: I’m enjoying this part of the course and I really think some of the concepts are starting to sink in.

Cheers,
Tele

Current progress of room :slight_smile:

#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "OpenDoorTry2.h"
#include "GameFramework/Actor.h"

UOpenDoorTry2::UOpenDoorTry2()
{
	PrimaryComponentTick.bCanEverTick = true;
}

void UOpenDoorTry2::BeginPlay()
{
	Super::BeginPlay();
	
	InitialYaw = GetOwner()->GetActorRotation().Yaw;

	CurrentYaw = InitialYaw;
	UE_LOG(LogTemp, Warning, TEXT("Initial Yaw for %s is %f"), *GetOwner()->GetName(), InitialYaw);
	TargetYaw = InitialYaw + TargetYaw;
	UE_LOG(LogTemp, Warning, TEXT("TARGET YAW for %s IS %f"),*GetOwner()->GetName(), TargetYaw);

	if(!PressurePlate)
	{
	UE_LOG(LogTemp,Warning, TEXT("PRESSURE PLATE NOT INITIALIZED ON %s"), *GetOwner()->GetName());
	}
	if(!PressurePlate2)
	{
	UE_LOG(LogTemp,Warning, TEXT("PRESSURE PLATE2 NOT INITIALIZED ON %s"), *GetOwner()->GetName());
	}

	ActorThatOpensTheDoor = GetWorld()->GetFirstPlayerController()->GetPawn();
}

void UOpenDoorTry2::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	CurrentWorldTime=GetWorld()->GetTimeSeconds();
	
	if (PressurePlate && PressurePlate->IsOverlappingActor(ActorThatOpensTheDoor) || PressurePlate2 &&PressurePlate2->IsOverlappingActor(ActorThatOpensTheDoor))
	{
		OpenDoor(DeltaTime);
		DoorLastOpened=GetWorld()->GetTimeSeconds();
	}
	else
	{
		if (CurrentWorldTime >= DoorLastOpened + DoorCloseDelay)
		{
			CloseDoor(DeltaTime);
		}
	}
}
void UOpenDoorTry2::OpenDoor(float DeltaTime)
{
	CurrentYaw = FMath::FInterpTo(CurrentYaw, TargetYaw, DeltaTime, 2);
	FRotator DoorRotation = GetOwner()->GetActorRotation();
	DoorRotation.Yaw= CurrentYaw;
	GetOwner()->SetActorRotation(DoorRotation);	
}

void UOpenDoorTry2::CloseDoor(float DeltaTime)
{
	CurrentYaw = FMath::FInterpTo(CurrentYaw, InitialYaw, DeltaTime, 4);
	FRotator DoorRotation = GetOwner()->GetActorRotation();
	DoorRotation.Yaw= CurrentYaw;
	GetOwner()->SetActorRotation(DoorRotation);	
}


#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Engine/TriggerVolume.h"
#include "OpenDoorTry2.generated.h"

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE427_API UOpenDoorTry2 : public UActorComponent
{
	GENERATED_BODY()

public:	

	UOpenDoorTry2();

protected:

	virtual void BeginPlay() override;

public:	

	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	void OpenDoor(float DeltaTime);
	void CloseDoor(float DeltaTime);

private:

	float InitialYaw;
	
	float CurrentYaw;

	UPROPERTY(EditAnywhere)	
	float TargetYaw;

	UPROPERTY(EditAnywhere)
	ATriggerVolume* PressurePlate;

	UPROPERTY(EditAnywhere)
	ATriggerVolume* PressurePlate2;

	UPROPERTY(EditAnywhere)
	AActor* ActorThatOpensTheDoor;

	float DoorLastOpened = 0.f;

	UPROPERTY(EditAnywhere)
	float DoorCloseDelay =2.f;

	float CurrentWorldTime=0.f;
};

Code so far! :smiley: Feeling good!

Ah, the code doesn’t all copy and paste too well, I see :smiley: Had a blast with this course! Looking forward to Toon Tanks!!

// 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 "Components/InputComponent.h"


#include "THEACTORSGRABBER.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE427_API UTHEACTORSGRABBER : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UTHEACTORSGRABBER();
public:
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

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


private:

	float Reach = 150.f;
	UPhysicsHandleComponent* PhysicsHandle = nullptr;
	UInputComponent* InputComponent = nullptr;
	void Grab();
	void Release();
	void FindPhysicsHandle();
	void SetupInputComponent();

	// Return the first actor within reach with physics body
	FHitResult GetFirstPhysicsBodyInReach() const;


	// Return LineTraceEnd

	FVector GetReach() const;
	FVector PlayerViewPointLocationFunction() const;
};

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

#include "GameFramework/PlayerController.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "THEACTORSGRABBER.h"

#define OUT

// Sets default values for this component's properties
UTHEACTORSGRABBER::UTHEACTORSGRABBER()
{
	// 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 UTHEACTORSGRABBER::BeginPlay()
{
	Super::BeginPlay();
	
	UE_LOG(LogTemp, Warning, TEXT("GRABBER ONLINE"));
	FindPhysicsHandle();
	SetupInputComponent();
}

void UTHEACTORSGRABBER::SetupInputComponent()  // Set up the Input Component for Binding Key Presses
{
InputComponent = GetOwner()->FindComponentByClass<UInputComponent>();

	if(InputComponent)
		{
			InputComponent->BindAction("Grab",IE_Pressed, this, &UTHEACTORSGRABBER::Grab);
			InputComponent->BindAction("Grab",IE_Released, this, &UTHEACTORSGRABBER::Release);
		}
}

void UTHEACTORSGRABBER::FindPhysicsHandle() // Check for Physics Handle Component:
{
	PhysicsHandle = GetOwner()->FindComponentByClass<UPhysicsHandleComponent>();

		if (!PhysicsHandle)
		{
			UE_LOG(LogTemp, Error, TEXT("NO PHYSICS HANDLE WAS FOUND ON %s"), *GetOwner()->GetName());
		}
}

void UTHEACTORSGRABBER::Grab()
{
	// Only Ray cast when key is pressed

	FHitResult HitResult = GetFirstPhysicsBodyInReach();
	UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
	AActor*ActorHit = HitResult.GetActor();

	// Try to reach any actors with a "physics body" collision channel set.
	// If we hit something, then attach the physics handle:

	if(ActorHit)
		{
			if(!PhysicsHandle)	{return;}
		PhysicsHandle->GrabComponentAtLocation(
		ComponentToGrab,
		NAME_None,
		GetReach()
		);
		}
}

FVector UTHEACTORSGRABBER::GetReach() const
{
	FVector PlayerViewPointLocation;
	FRotator PlayerViewPointRotation;

	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint
	( 
		OUT PlayerViewPointLocation, 
		OUT PlayerViewPointRotation
	);

	return PlayerViewPointLocation + PlayerViewPointRotation.Vector() * Reach;
}

void UTHEACTORSGRABBER::Release()
{
	UE_LOG(LogTemp, Error, TEXT("Release PRESSED"));
	//  Remove (release) the physics handle:
	if (!PhysicsHandle) {return;}
	PhysicsHandle->ReleaseComponent();
}

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

	// If the physics handle is attached, move the object we are holding
	
	if(PhysicsHandle->GrabbedComponent)
	{
		if (!PhysicsHandle) {return;}
		PhysicsHandle->SetTargetLocation(GetReach());
	}
}

FHitResult UTHEACTORSGRABBER::GetFirstPhysicsBodyInReach() const
{
	DrawDebugLine
	(
		GetWorld(),
		PlayerViewPointLocationFunction(),
		GetReach(),
		FColor(0, 255, 0),
		false,
		0.f,
		0,
		5.f
	);

	// RAY CAST! ( REACH ) 

	FHitResult Hit;
	FCollisionQueryParams TraceParams(FName(TEXT("")), false, GetOwner());

	GetWorld()->LineTraceSingleByObjectType(
		OUT Hit,
		PlayerViewPointLocationFunction(),
		GetReach(), 
		FCollisionObjectQueryParams(ECollisionChannel::ECC_PhysicsBody),
		TraceParams
	);

	// What was hit by reach?
	AActor* ActorHit =	Hit.GetActor();
	if (ActorHit)
	{
			UE_LOG(LogTemp, Warning, TEXT("%s WAS HIT BY LINE TRACE"), *ActorHit->GetName());
	};
	return Hit;
}

FVector UTHEACTORSGRABBER::PlayerViewPointLocationFunction() const
{
// Get the Player's Viewpoint:
	FVector PlayerViewPointLocation;
	FRotator PlayerViewPointRotation;

	GetWorld()->GetFirstPlayerController()->GetPlayerViewPoint( 
		OUT PlayerViewPointLocation, 
		OUT PlayerViewPointRotation
	);
	return PlayerViewPointLocation;

	// UE_LOG(LogTemp, Warning, TEXT
	// 		(
	// 			"ViewpointLocation is %s, rotation is %s"
	// 		), 
	// 			*PlayerViewPointLocation.ToString(), 
	// 			*PlayerViewPointRotation.ToString()
	// );

	//^^ Log, to test
}
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

#include "OpenDoorTry2.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE427_API UOpenDoorTry2 : public UActorComponent
{
	GENERATED_BODY()

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

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

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

	bool OpenDoorSound = false;



private:

	float InitialYaw;
	float CurrentYaw;

	UPROPERTY(EditAnywhere)	
	float TargetYaw;

	UPROPERTY(EditAnywhere)
	ATriggerVolume* PressurePlate = nullptr;

	UPROPERTY(EditAnywhere)
	ATriggerVolume* PressurePlate2 = nullptr;

	UPROPERTY(EditAnywhere)
	AActor* ActorThatOpensTheDoor = nullptr;

	float DoorLastOpened = 0.f;

	UPROPERTY(EditAnywhere)
	float DoorCloseDelay =2.f;

	float CurrentWorldTime = 0.f;

	UPROPERTY()
	UAudioComponent* AudioComponent = nullptr;
};

#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Components/AudioComponent.h"
#include "OpenDoorTry2.h"
#include "GameFramework/Actor.h"
#include "Components/PrimitiveComponent.h"

UOpenDoorTry2::UOpenDoorTry2()
{
	PrimaryComponentTick.bCanEverTick = true;
}

void UOpenDoorTry2::BeginPlay()
{
	Super::BeginPlay();
	
	InitialYaw = GetOwner()->GetActorRotation().Yaw;

	CurrentYaw = InitialYaw;
	UE_LOG(LogTemp, Warning, TEXT("Initial Yaw for %s is %f"), *GetOwner()->GetName(), InitialYaw);
	TargetYaw = InitialYaw + TargetYaw;
	UE_LOG(LogTemp, Warning, TEXT("TARGET YAW for %s IS %f"),*GetOwner()->GetName(), TargetYaw);

	if(!PressurePlate)
	{
	UE_LOG(LogTemp,Warning, TEXT("PRESSURE PLATE NOT INITIALIZED ON %s"), *GetOwner()->GetName());
	}
	if(!PressurePlate2)
	{
	UE_LOG(LogTemp,Warning, TEXT("PRESSURE PLATE2 NOT INITIALIZED ON %s"), *GetOwner()->GetName());
	}


	// ActorThatOpensTheDoor = GetWorld()->GetFirstPlayerController()->GetPawn();
}

void UOpenDoorTry2::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	CurrentWorldTime=GetWorld()->GetTimeSeconds();

	if (!ActorThatOpensTheDoor)
	{
		UE_LOG(LogTemp,Warning, TEXT("NO ACTOR ASSIGNED TO OPEN DOOR"));
		return;
	}
	
	if(TotalMassOfActors() > 100.f && PressurePlate2 &&PressurePlate2->IsOverlappingActor(ActorThatOpensTheDoor))
	{
		OpenDoor(DeltaTime);
		DoorLastOpened=GetWorld()->GetTimeSeconds();
	}
	else
	{
		if (CurrentWorldTime >= DoorLastOpened + DoorCloseDelay)
		{
			CloseDoor(DeltaTime);
		}
	}
	// if (PressurePlate && PressurePlate->IsOverlappingActor(ActorThatOpensTheDoor) || PressurePlate2 &&PressurePlate2->IsOverlappingActor(ActorThatOpensTheDoor))
	// {
	// 	OpenDoor(DeltaTime);
	// 	DoorLastOpened=GetWorld()->GetTimeSeconds();
	// }
	// else
	// {
	// 	if (CurrentWorldTime >= DoorLastOpened + DoorCloseDelay)
	// 	{
	// 		CloseDoor(DeltaTime);
	// 	}
	// }
	FindAudioComponent();

}
	
void UOpenDoorTry2::FindAudioComponent()
{
	AudioComponent = GetOwner()->FindComponentByClass<UAudioComponent>();
	if (!AudioComponent)
		{
			UE_LOG(LogTemp, Error, TEXT(" %s MISSING AUDIO COMPONENT"), *GetOwner()->GetName());
			return;
		}
}


void UOpenDoorTry2::OpenDoor(float DeltaTime)
{
	CurrentYaw = FMath::FInterpTo(CurrentYaw, TargetYaw, DeltaTime, 2);
	FRotator DoorRotation = GetOwner()->GetActorRotation();
	DoorRotation.Yaw= CurrentYaw;
	GetOwner()->SetActorRotation(DoorRotation);	
	if(!AudioComponent){return;}
	if (!OpenDoorSound)
	{
	AudioComponent->Play();
	OpenDoorSound=true;
	}
}

void UOpenDoorTry2::CloseDoor(float DeltaTime)
{
	CurrentYaw = FMath::FInterpTo(CurrentYaw, InitialYaw, DeltaTime, 4);
	FRotator DoorRotation = GetOwner()->GetActorRotation();
	DoorRotation.Yaw= CurrentYaw;
	GetOwner()->SetActorRotation(DoorRotation);	
	if (OpenDoorSound)
	{
		OpenDoorSound = false;
	}
}

float UOpenDoorTry2::TotalMassOfActors() const
{
	float TotalMass =0.f;

	// Find all overlapping Actors
	TArray<AActor*> OverlappingActors;
	if(!PressurePlate)
	{
	UE_LOG(LogTemp,Warning, TEXT("PRESSURE PLATE NOT INITIALIZED ON %s"), *GetOwner()->GetName());
	return TotalMass;}
	PressurePlate->GetOverlappingActors(OverlappingActors);


	// Add up the masses
	for(AActor*Actor : OverlappingActors)
	{
		TotalMass +=Actor->FindComponentByClass<UPrimitiveComponent>()->GetMass();
	}

	return TotalMass;


}