Building Escape Game

This topic will be used to track my progress through the Building Escape Game development, once the game is finished I will post a link to a downloadable version of it here.

EDIT:

My project has finally been completed, after a major out of state move and then Thanksgiving, I successfully completed the project.

Derelict - A Ship Escape

1 Like

A tiny bit of the ways in, and getting excited about the possibilities of this game. Currently learning about importing fbx files and setting up materials. If I am not careful I could spend a lot of time playing with the node based material editor - this things cool.

Anyways, My door and door frame are setup in their initial design.

2 Likes

Room 1 has been designed with BSPs with the entirety of the room turned into a single Static Mesh. I then learnt how to set the collision on the Static Mesh to use the complex geometry of the mesh itself.

Lighting definitely needs work, but I am happy with the complexity of the room design as it challenged me to have a better understanding of both the BSPs and the geometry editing tools.

2 Likes

I also spent a little time looking into the Material creator. Hooked up all specular maps for both the “ReinforcedWood” and “CastleBrick” materials. I also struggled through learning how to use the Height maps. They are a bit more complicated but after research and trial and error I managed to get the Height maps for my materials setup.

A big challenge I ran into was getting an error when trying to use the BumpOffset node. Basically the error was telling me I could not use a float3 as it expected a float1. This was caused because I was sending the height maps RGB which had 3 float points in it (R,G,B).

Your height input needs to be float1. Even with a RGB greyscale texture you are supposed to use just one of its channels. Meaning all you need to do is connect either the R, G, or B outputs of the height TextureSample node to the Height input of the BumpMap.

You should then connect the output of the BumpOffset to all UVs of the other TextureSample nodes.

Initial Lighting is done for Room1. I encountered a challenge when setting up the lighting, but through diligent googling I found the answer.

When building the lighting for my map the logs showed that my room had 40% overlapping UVs. As said previously, I had no idea how to fix this and played with the lighting for about an hour. I looked at Unreal forums and tried nearly every purposed solution. Of course like most issues, the fix was relatively simple, I needed to increase the Light Map Resolution and set the proper Light Map Coordinate Index for the Static Mesh.

image

Upon making those adjustments and rebuilding my light settings, I was able to clear the error and make the lighting of the room look much better.

While I still have yet to figure out my “story” for the game, I want to go with a dark and spooky ambience (in the spirit of Halloween of course). The lighting will need future work, but as a starting point I believe it helps achieve the desired feel.

Worked on the materials some more allowing for separate UV scaling (via Scalar Parameters) for the ground and walls.

1 Like

I am probably getting ahead of myself, but I decided I wanted my door to be able to open and close - specifically when we add interaction. To go ahead and prepare for this eventuality I setup two additional private variables in the OpenDoor.h file - ClosedYaw, and OpenYaw. These values store the rotation the door should be at when closed or open.

As you will see in my code, ClosedYaw is being set to the Actors current yaw at the start of the game, and then the OpenYaw is being set to the ClosedYaw + 90 degrees.

I then set the TargetYaw to be the OpenYaw, so that when the lerp happens it will open the door. In future the TargetYaw will be determined based on if the door is open or closed. Open doors will have a TargetYaw of ClosedYaw, and Closed doors will have a TargetYaw of OpenYaw.

OpenDoor.h

// Copyright Mathew Nold 2020

#pragma once

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

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

public: 

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

protected:

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

public: 

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

private:

    float OpenYaw = 90.f;
    float ClosedYaw = 0.f;
    float TargetYaw = OpenYaw;

    bool bIsOpen = false;

};

OpenDoor.cpp

// Copyright Mathew Nold 2020

#include "OpenDoor.h"
#include "GameFramework/Actor.h"

// 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();

    ClosedYaw = GetOwner()->GetActorRotation().Yaw;
    OpenYaw = ClosedYaw + 90.f;
    TargetYaw = OpenYaw;
}

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

    // FRotator OpenDoor{0.f, 90.f, 0.f};
    // GetOwner()->SetActorRotation(OpenDoor);

    float CurrentYaw = GetOwner()->GetActorRotation().Yaw;
    // UE_LOG(LogTemp, Warning, TEXT("TargetYaw: %f"), TargetYaw);
    // UE_LOG(LogTemp, Warning, TEXT("%s Current Yaw: %f"), *GetOwner()->GetName(), CurrentYaw);

    if (CurrentYaw != TargetYaw)
    {
        float NewYaw = FMath::FInterpTo(CurrentYaw, TargetYaw, DeltaTime, 2);
        FRotator OpenDoor{GetOwner()->GetActorRotation().Pitch, NewYaw, GetOwner()->GetActorRotation().Roll};

        GetOwner()->SetActorRotation(OpenDoor);
    }
}

Additionally I was concerned about running the Lerp function every update tick, even when the door has already achieved the TargetYaw. To get around this for now I simply wrapped the Lerp functionality into an If statement that checks the CurrentYaw against the TargetYaw. Due to my intentions for TargetYaw to change, I determined that when implementing the interaction system, the interaction will set the doors TargetYaw to OpenYaw or ClosedYaw based on a boolean variable bIsOpen. This simple code would be all I need during the interaction to make the door open/close.

if (bIsOpen)
{
TargetYaw = ClosedYaw
}
else
{
TargetYaw = OpenYaw
}

Just finished a big out of state move! I am now living closer to potential job opportunities and a much better environment to raise my family!

Now I am ready to crank out some exciting games and continue to grow as a Game Developer.

Today I am working on Lessons 110 - 120.

As of this post I am currently on Lesson 115, but I will recap the previous lessons.

Lesson 110: Protecting From A Null Pointer
I improved my error handling by checking to ensure a PressurePlate actor is connected to the OpenDoor component, and logging an Error if it is not so that I can easily find the null pointer.

Lesson 111: Getting The Player To Open The Door
The door still had to have the player manually setup on the OpenDoor component for the actual functionality of opening the door to happen. This was resolved by updating the code to search the world for the PlayerController, and get the Pawn attached to the PlayerController component. Now I no longer need to worry about setting my player manually!

Lesson 112: Getting the Door To Close
Thanks to some forward thinking on my part, I already had setup most of the code needed for this functionality to happen. I strayed from the two function method used by the instructor and instead updated my OpenDoor function to a more generic MoveDoor function. Then using If statements based on if the player is inside or outside the collision volume (PressurePlate), I set the TargetYaw to either OpenYaw or ClosedYaw (2 exposed properties on the OpenDoor component).Some additional exposed properties were added as well to give a ton of control to the designer (I will cover this in a second)

Lesson 113: Using GetTimeSeconds()
In this lesson we learned how to access the global time from the start of the game. Fantastic, now I can factor things based on time with just some simple math. Following along with the instructor I created a delay for closing the door. Being that I needed to do a good amount of play testing to get the door to almost make you think you can escape before it closed on you, I exposed this property to the OpenDoor component as well.

Lesson 114: Designer Friendly Components
As a long time developer who has worked with many different teams of people I am pretty used to exposing variables (properties) for easy tweaking. I had already been exposing every reasonable property that I found myself tweaking multiple times. Additionally I decided to add a few more exposed properties such as separate door movement speeds based on if you are opening or closing the door. As well as a Boolean that determines if the door is able to close at all, meaning once the door opens it stays open! As a bonus I found out how to disable editing of variables related to closing the door if the door can not be closed.

UPROPERTY(EditAnywhere, meta=(EditCondition="bCanClose"))

Lesson 115: Grabbing System Overview
We are starting the work on creating a grabbing system for picking up actors and moving them around the level! See below for some of my thoughts on ways I could go about making the grabbing, and weight system.

Think critically about how you would go about allowing a player pawn to pickup other objects and move them around the world.

1. When a player approaches an actor that can be picked up, it should be highlighted with an outline color.
2. The player then presses, and holds the left mouse button to grab and hold onto said actor.
3. The actor will go to the center of the players camera, and its collision will be turned off temporarily.
4. When letting go of the actor its collision will be turned back on and it will be released from the pawn.
5. The pressure plate will need to have a component made for it that checks the total weight within the collision volume against a designer set required weight. 
6. When the weight limit is met the door connected to the pressure plate will open.

How to determine weight?
1. All grabbable actor within a room are apart of an array set on the pressure plate, all of them would be required within the collision volume to activate the pressure plate
2. Weight values are given to all grabbable actors and a total weight is added together based on all grabbable actors on the pressure plate. If the total weight is greater or equal to the pressure plate weight requirement the pressure plate activates

Possible ways to move the grabbed object?
1. Get the location of the pawn and an offset and set the position of the grabbed actor to that equation
2. Parent the grabbed actor to the pawn

How may you know what to grab?
1. Put a component on each grabbable actor - this component will also be responsible for determine weight and highlighting the actor
All grabbable actors within a room are apart of an array set on the pressure plate

Derelict - My building escape game, is finished.

Of course it is very short, with just a few rooms, but I challenged myself in a number of exciting ways.

Firstly I decided to move away from the Medieval theme, I jumped into my time machine and advanced far into the future - the age of space exploration and science fiction! In doing this I realized the door that we built did not give me a feeling of futuristic and so I rewrote the code used for moving the door. I designed a double door sliding system that instead of rotating the door, moves the position of the door. This allowed me to create a hydraulic door effect. Additionally other general improvements/updates were made to the door code that allows for greater flexibility during level design.

Level Design was a challenge in itself. Coming from a programming background, the coding comes fairly easy to me, however designing engaging levels takes some effort. My goal was to give a sense that you are on a crashed space ship and you must move crates to Trigger zones to be able to open doors and move on, with the goal of getting off the ship. I am proud to say, while there are far better level designs in the galaxy, my goal was accomplished as far as I am concerned. Using limited assets from the free Polar Sci-Fi Facility pack I was able to give a sense of a crumbling ship interior.

Please give the game a go and feel free to leave feedback. I feel like I am getting better at level design, but I have a loooooong way to go, and the feedback will help power my thrusters.

Privacy & Terms