TIL: Learn to Code in C++ by Developing Your First Game (Section 3, Lectures 76-80)

I set out yesterday to start playing Giana Sisters: Twisted Dreams, which is on my list of games I own which I should be playing so that I can force myself to learn Japanese. I don’t know what to say… I played the first two levels, and I just had no fun at all. And there are a ton more menu screens than there were in Brothers. Part of me wants to stay focused and translate that shit… but it’s not going to be fun, and if I’m not having fun in my leisure hours, what’s the point? So instead of torturing myself playing that game, I’m taking it off my To Play list, and moving on.

And in this case, moving on means doing more tutorials.

Section: 3 – Building Escape – Your First Unreal / C++ Game

Lecture 76: Resetting Your Unreal Project

If things break, we fall back on the version control to go back to a working file. The steps are as follows:

1. in perforce, we roll back to a working revision
2. delete derived folders and files – leave the config, content, and source folders and the .uproject file
3. re-open ue4 from either the launcher or .uproject file (this will also recreate “generated.h” files in Intermediate
4. regenerate your IDE projects

Lecture 77: Using FindComponentByClass()

We add a Physics Handle Component onto the DefaultPawn_BP. The PhysicsHandleComponent is an object for “grabbing” and moving physics objects around while allowing the object you are grabbing to continue to use physics. An example of this could be in the form of a “gravity gun” where you can pick-up and drop physics objects. ((Physics Components))

We want to look for our attached Physics Handle during BeginPlay, and since we want to find it on the object that the grabber is attached to, we will GetOwner.

PhysicsHandle = GetOwner()

This returns a pointer to an actor, so we want to find the component by class.

PhysicsHandle = GetOwner()->FindComponentByClass();

The header file of course needs to have created that PhysicsHandle by adding a private: UPhysicsHandleComponent* PhysicsHandle = nullptr;

We can’t just assume it works, we need to add debug information in case it doesn’t. Log an Error to help us find whether it has failed.

if (PhysicsHandle)
{
     // Physics handle is found
}
else
{
     UE_LOG(LogTemp, Error, TEXT("Unable to find a physics handle on %s"), *GetOwner()->GetName());
}

Lecture 78: Introducing Input Binding

We add the ability to use the keyboard/mouse/controller so that we can press a button and do something. Input Components can be viewed on our Default Pawn at run-time, but are non-existent on the blueprint itself.

We set-up something similar to the physics handle above, but with a UInputComponent. We then use a UE_LOG to prove that it’s connected, since it’s difficult to actually disconnect it.

Once we’re positive it’s working, we go to Project Settings->Input so that we can add some controls. We add a “Grab” Action Mapping, one will be “Left Shift”and the other will be “Right Mouse Button”. This will allow us to use either key to trigger our grab action when we make it.

Then, instead of using UE_Log to prove we have everything connected properly, we want to bind the input. In this case, we use:

/// Bind the input action
InputComponent->BindAction("Grab", IE_Pressed, this, &UGrabber::Grab);

The final part of that will be discussed in the next video… but basically it’s a method where &UGrabber is a Reference, and Grab will be the name of the method. It doesn’t exist yet at this point in time, so we need to go back to the .h file to create it.

The .h simply includes private:
// Ray-cast and grab what's in reach
void Grab();

While the .cpp includes the definition:
void UGrabber::Grab() {
    UE_LOG(LogTemp, Warning, TEXT("Grab Pressed."))
}

Lecture 79: Accessors & Memory Layout

& is used for References and means essentially “at the address of”.

If we have information which is a class, an enum, or a namespace, we need to access that information using ::
For example: UGrabber::Grab get the Grab function found at the address of UGrabber. std::cout finds the cout function that is part of std. OK will find the OK Enum which is part of the EWordStatus. This is all “permanent storage” and shouldn’t be moved around at run-time.

If something is an instance or reference, we access it using a .
For Example, MyGrab.Grab(), MyBullCowCount.Bulls, MyGrabRef.Grab()

If something is a pointer, we use ->
MyGrabPtr->Grab()
MyGrabPtr->Reach

. and -> can be used for information that may change during run-time.

We create a Release() which is practically identical to Grab(), however we use IE_Released instead of IE_Pressed.

Lecture 80: Reducing Code in “Hot Loops”

Whee! Refactoring!

A “hot loop” is code that is called often. TickComponent is an example of code that is called every frame. You need to be cautious of creating code that gets created too often. So now we want to refactor what we’ve done to make it optimized.

Ideally, the grab/release should be public so that we can have a “Character Controller” trigger stuff remotely… for simplicity we’re leaving it private in this example. So we won’t touch the header file.

In the .cpp there are a few things we can do such as deleting any empty comment lines which were created by UE when we made the component… or white space. This streamlines visually, but doesn’t really do anything for the computer.

We can get rid of logs that we don’t need, such as “Grabber reporting for duty”.

Looking for the physics handle can be moved to its own method so that we can try and simplify “BeginPlay()” into something more readable. We can move that section to the bottom, and declare our FindPhysicsHandleComponent() appropriately in the .h and .cpp files

It is important to constantly build and test while refactoring to make sure that nothing gets broken. It’s better to compile in Unreal as the error messages tend to be more helpful.

In this case, we’ll get an error as we need to put the class infront of the method name. This means we don’t define “FindPhysicsHandleComponent()” but rather we define “UGrabber::FindPhysicsHandleComponent”. This sort of error will be much more difficult to make if you use Visual Studio’s assistance to define methods. Instead of doing it manually, declare it in the .h, then right click and use the Create Definition option to let it automatically prefix the appropriate class.

We want to repeat the process for setting up the input component.

Commenting helps to make things more readable, as you can use the + / – buttons on methods to collapse them to help make the code even more human readable. In our case, we move all declarations related to the BeginPlay() to just after that method, and before we begin with TickComponent.

In the Tick Component, we simply want to do:

// if the physics handle is attached
    // move the object that we're holding

Which means anything else (we get rid of the DrawDebugLine since it’s unnecessary) already there should go somewhere else so that we aren’t calculating it all the time.

In the header we create:

// Return hit for first physics body in reach
const FHitResult GetFirstPhysicsBodyInReach();

We can right click to define, and move all the code (that we’ve created) into this that was in tick. Now, if we call this method from our Grab(), we can do the ray-cast ONLY when we press the grab button, and NOT EVERY FRAME.