My Building Escape

urc_s03_section_3_wrapup

#1

This is my building escape. Some of the mechanics need polishing…


#2

Nice… Love the secret lever in the wall lamp!


#3

How did you do that lever? I tried to do something similar with a button but got smashed by my lack of knowledge. Tried to find collisions between ray and objects with an assigned tag, but failed horribly to do something like that because my ray was finding everything without any filtering. Would appreciate if you were to give some hints.


#4

I also tried and failed miserably. Actually i did not consider the raycast approach, will try that and see what happens.


#5

I’d be happy to put up how I did it, but I can’t remember exactly and won’t be home for another few days.

Happy New Year!

-Ricky


#6

We would love it if you would!
I’m patient :slight_smile:
Thank you!


#7

Hi All,

It’s been a long time since I worked on the building escape, so I had to go back and remember what I’d done. It looks like I created two new components: a puzzleHandle and a puzzleTarget. The puzzleHandle keeps a list of associated puzzleTargets and when the handle is triggered it calls a “doPuzzleBehavior” function on each of the puzzleTargets. This was all built in C++. Once it’s working, you add the component to the object and then populate a list of targets in the component’s details page. I also updated the code in the grabber component to check for puzzle handles.

I’ve copied in some of the code below. It’s actually unfinished. If I remember right, I wanted to keep a list of potential behaviors and have the handle associate a particular behavior with each target. I never implemented that code.

Here’s the meat of the puzzleHandler.h :

UENUM()
enum Behaviors
{
Rotate90 UMETA(DisplayName = “Rotate 90”),
};

USTRUCT()
struct FActorTarget
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere)
AActor* Target;

FActorTarget()
{
	Target = nullptr;
}

};

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class BUILDINGESCAPE_API UPuzzleHandle : public UActorComponent
{
GENERATED_BODY()

public:
// Sets default values for this component’s properties
UPuzzleHandle();

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(EditAnywhere, Category = Behaviors)
	TEnumAsByte<Behaviors> PuzzleBehavior;

void DoPuzzleBehavior();

public:

UPROPERTY(EditAnywhere)
	TArray<FActorTarget> PuzzleTargets;

};

The doPuzzleBehavior get's called, checks the assigned behavior for this puzzle (at this point, one behavior per puzzle handle) handle and then calls the doPuzzleBehavior function for each of the targets:

void UPuzzleHandle::DoPuzzleBehavior()
{
switch (PuzzleBehavior)
{
case Behaviors::Rotate90:
FRotator Rot = GetOwner()->GetActorRotation();
FString RotStr = Rot.ToString();

	UE_LOG(LogTemp,Warning,TEXT("Rotation: <%s>"),*RotStr)
	
	FRotator NewRotation = FRotator(0.0f,-90.0f,-90.0f);
	GetOwner()->SetActorRotation(NewRotation);

	for (FActorTarget& Tgt : PuzzleTargets)
	{
		UE_LOG(LogTemp, Warning, TEXT("Target is %s."), *Tgt.Target->GetName())

		if (Tgt.Target->FindComponentByClass<UPuzzleTarget>())
			{
				UE_LOG(LogTemp, Warning, TEXT("Target %s has a PuzzleTarget."), *Tgt.Target->GetName())
				Tgt.Target->FindComponentByClass<UPuzzleTarget>()->DoPuzzleBehavior();
			}
	}
	
	break;
//default:

}

The Target side's DoPuzzleBehavior:

void UPuzzleTarget::DoPuzzleBehavior()
{
FRotator Rot = GetOwner()->GetActorRotation();
FString RotStr = Rot.ToString();

UE_LOG(LogTemp, Warning, TEXT("Doing PuzzleTarget Puzzle Behavior for %s. Rotation: <%s>"), *GetOwner()->GetName(), *RotStr)

	FRotator NewRotation = FRotator(0.0f, -270.0f, 0.0f);
GetOwner()->SetActorRotation(NewRotation);

}


And finally, the modified grabber code:

void UGrabber::Grab()
{
FHitResult MyHit;

///Try to reach out to any actors with Physics body collision channel set
auto HitResult = GetFirstPhysicsBodyInReach();
UPrimitiveComponent* ComponentToGrab = HitResult.GetComponent();
auto ActorHit = HitResult.GetActor();

///If we find one, attach a physic handle.
if (ActorHit)
{
	UE_LOG(LogTemp, Warning, TEXT("%s is Grabbing %s."), *GetOwner()->GetName(), *ActorHit->GetName() )
	PhysicsHandle->GrabComponentAtLocationWithRotation(ComponentToGrab,
		NAME_None,
		HitResult.Location,
		ActorHit->GetActorRotation());

	AActor* Owner = nullptr;
	UPuzzleHandle* PuzzleHandleComponent = nullptr;

	Owner = ActorHit;
	if (Owner != nullptr)
	{
		UE_LOG(LogTemp, Warning, TEXT("%s's Owner is %s."), *ActorHit->GetName(), *Owner->GetName())
		PuzzleHandleComponent = Owner->FindComponentByClass<UPuzzleHandle>();

		if (PuzzleHandleComponent != nullptr)
		{
			UE_LOG(LogTemp,Warning, TEXT("Found a PuzzleHandle!"))
			PuzzleHandleComponent->DoPuzzleBehavior();
			Release();
		}

	}
	else
		UE_LOG(LogTemp, Warning, TEXT("%s has no Owner."), *ActorHit->GetName())
}

}


Hope that's helpful to someone.

-Ricky

#8

this is actually amazing.
I tried to wrap my head around it but at my current state, its bigger then me.
i’m currently trying to figure out how to set up a chain reaction event between multiple object, starting with the pawn interacting with an actor.


#9

Depending on the events that you want to happen, you could make a single component that provides the behaviors in the chain of events. I don’t think you need a “puzzlehandle” and a “puzzletarget”. I did that so I could distinguish between the trigger of the action and the item where I wanted the action to happen. Those could be combined pretty easily.

I think having a single component is a good idea, because then you can standardize behaviors across lots of kinds of objects.

Show us what you come up with for your chain of events.


#10

Well, maybe “chain of events” is a bit of an exaggeration. Basically I wanted to create a lever, and when pulled, it activates a platform that springs upwards and can propel objects. I have the platform working, and the pawn can interact with the lever, but I’m not sure how to associate the platform with the lever, and only activate the platform as the is being pulled.


#11

And as I was typing this message, I thought to create a component inside the lever, and call it “Activator”.
inside I create two function and a bool.
bool bIsActive;
bool GetActive();
bool SetActive(bool ActiveState);
what they do should be pretty straightforward.

After i line trace for a static mesh and get the hit result pointing the an actor, I set an object and use FindComponentByClass to find if it has a UActivator component in it.
if it does, upon the press of a certain button, I set bIsActive to true using SetActive Function.
and then I do the same thing through the platform, and this time I only need to check for bIsActive through GetActive().
Man. I am so pleased with myself.