Learned This Week
Overview
This week saw the beginning of one course and the end of another. I got to learn about some interesting parallels between the Unreal C++ code and the blueprint side of things by looking at metadata, structs, and binding user input. Because I'm familiar and comfortable with blueprint scripting, it's neat to learn about the code behind it - for instance, why the Development Only warning appears on the Print String and Print Text nodes.
Finishing up the Converting Blueprint to C++ course offered by Unreal this week, I dove into binding player input in C++ - I find it odd that the nodes in blueprint for action mappings allow you to pull off of themselves for each manner of activation (i.e. pressed and released) but that you need to bind them separately in C++. However, when I think about it, it makes a lot of sense, insomuch as you can only bind one input to one function.
I began a course from GameDev.tv this week called "Unreal 5.0 Developer: Learn C++ and Make Video Games" - I've touched the course before and got about 1/3 of the way through it, and so I'll be starting at the point at which I left off (a few videos behind of course in order to refresh my memory about the code goal and the project). It was quite refreshing to be following along with code in UE 5 again, as the previous course has been in UE 4. I'll be beginning another course next week offered by Unreal called "Balancing Blueprint and C++ in Game Development" which is exciting as well!
Key Points:
- Metadata Specifiers
- Metadata Specifiers (UMETA) are additional tags that can be attached to functions, variables, data members, etc in order to provide additional information about them and how they should be handled to the editor.
- UMETA tags are only available in and handled by the editor, meaning game code should not attempt to query UMETA.
- Specifiers like DisplayName (which replaces the default name of the node/pin with the text specified) and DevelopmentOnly (meaning the function will only run in Development mode - a good example of this is the print string node) can be extremely helpful when merging code between C++ and blueprint.
- USTRUCTs
- Similar to the ability to create blueprint-usable data structure types in the engine, you can create USTRUCTs in C++, which if defined with the BlueprintType specifier can be used in blueprints as well.
- Structs should be created in C++ if you ever think you might need to use them in C++, as C++ cannot access editor-created structs (and migrating structs is a pain!).
- User Input Bindings
- The functionality of Input Events in blueprint event graphs can be mirrored in C++ by using input bindings. Once an input is set up in the Project Settings, it can then be implemented in any blueprint that can receive input.
- Axis Mappings
- Setting up axis mappings is a bit more simple than action mappings, as they only have one possible trigger whereas action mappings have different manners of activation.
- User input is bound in a function on the class that can be overridden from its base class, the SetupPlayerInputComponent function which takes a UInputComponent pointer as its input.
- The first call in the function should be to set up the player input component, which can be seen below in the nitty-gritty.
- Once set up, methods can be called on the input component in order to bind input mappings to it.
- Action Mappings
- Similarly to axis mappings, action mappings can also be bound but require you to specify what manner of activation the binding should be connected to (pressed, released, repeat, double click, axis, and MAX).
- BlueprintCallable C++ Functions
- While BlueprintImplementableEvent and BlueprintNativeEvent allow C++ to interface with blueprint, marking a UFUNCTION as BlueprintCallable allows the C++ function to be called from blueprints, meaning C++ code can easily and flexibly be inserted into blueprint code.
- Drawing Debug Helpers
- Unreal has a lot of tools to help developers to figure out what is going on in-game behind the scenes. One of the most useful ways of doing so is to draw debug helpers - things like lines, text, spheres, capsules, and boxes.
- I've worked a lot with these in blueprint, and their functionality is more or less the same in C++ - they're a very powerful diagnostic tool, but they should not be used to draw actual geometry/in-game elements to the world.
Nitty-Gritty
- Programming With C++ UE Documentation - Jump to Top
- Metadata Specifiers
- Metadata can be added to C++ code to define how it interacts with parts of the engine and/or editor - this is editor-only, as game logic should not access meta
- There are existing tags usable for developers in order to have further control over C++ data and member usage
- Meta contains things like:
- DeprecatedNode (which indicates the class is deprecated and will show a warning when compiled),
- DisplayName (which replaces the default name of the node/pin with the text specified),
- Experimental (which labels the type as experimental/unsupported),
- Hidden (meaning it will not be displayed in the editor),
- DevelopmentOnly (meaning the function will only run in Development mode - a good example of this is the print string node),
- HideSelfPin (which hides the "Self" input pin on a BlueprintPure function),
- and many more helpful tags
- Meta is processed by the editor but not by the code - don't access meta at runtime
- Structs
- Custom data structures can be defined in C++ and can be used in both C++ and blueprint (when specified with the BlueprintType specifier)
- Structs support the use of UPROPERTYs but do not have member functions
- Unreal Converting Blueprint to C++ Course - Jump to Top
- Binding User Input
- We want to bind user input inside of the character that we're playing, so we'll first create a C++ base class for the existing character which is of a Character class type
- We'll be migrating over both axis and event inputs
- Character classes by default have a SetupPlayerInputComponent function which is used to bind axes and input events to functions & code
- In order to bind an axis or an event, we also need to include the Components/InputComponent.h header file
- We'll create a function for the Forward axis input defined in the project's input settings like so (at the moment we'll just have it print out what axis value it's getting):

- Once the function has been declared and defined, we then want to bind it as an axis in the SetupPlayerInputComponent function by using a member function on the PlayerInputComponent that is passed to the Setup function like this:

- Binding an input event is a bit more complicated - we're going to bind the Jump event - events in blueprint have multiple output types and those types are represented in C++ by an enumerator which is denoted along with the binding like so:

- The available enumerators are IE_Pressed, IE_Released, IE_Repeat, IE_DoubleClick, IE_Axis, and IE_MAX - for jumping, we want the Pressed option
- Example 3: FirstPersonCharacter
- We're going to migrate the input events from blueprint to C++ which we've already started to do
- One clear roadblock is the existing Grab input event which uses the Grabber component on the character
- If we were to migrate this, we would also need to migrate all of its parent components which would mean all of the character, and we don't want to do that
- Instead, we'll make it a BlueprintImplementableEvent and return the component in the blueprint graph
- Migrating the Forward input is next, the blueprint of which looks like this:

- The migration of which in C++ is fairly simple (though the GameFramework/CharacterMovementComponent.h header file needs to be included in order to call methods on the CharacterMovement as it is being done in blueprints):

- And we'll repeat that for the Right movement input as well:

- This allows us to move forward and backward as well as right and left - now we'll move on to the looking/camera movement calls which look like this in blueprint:

- Which are already calling functions in a parent class of Pawn, meaning we can just implement those directly like so:

- If we needed to do anything else in the middle, we would create another function to hold the additional functionality but since all we need to do is call a derived function, we can just bind it here directly
- In the blueprint for the Jump function, we are also already calling a parent function which is derived from the Super class of our created class, so we can bind it like so (and remove the now-defunct private function we created earlier):

- The last functions to migrate are the Grab and Release functions which look like this in blueprints:

- And in order to bind both the Pressed and Released actions, we need separate functions and bindings like so:


- These functions won't work right away, we need to add them in the Grabber class we created earlier:

- And migrate their implementation from blueprint to C++:
- Without forgetting to delete the matching functions from the BP_Grabber blueprint after compilation
- Course complete!
- Unreal 5: Learn C++ GameDev.tv Course - Jump to Top
- BlueprintCallable
- Functions in C++ can be easily exposed to be called in blueprints using the BlueprintCallable UFUNCTION specifier
- The function is then identified by Unreal as a node that can be called in blueprints

- Sometimes if you recompile with live coding and the function doesn't show up, you need to close the editor and build from the C++ project itself, then relaunch the editor project
- FindComponentByClass() & nullptr
- We want to be able to grab objects, and in order to do so we need to interact with the physics system from our player blueprint - we'll use a Physics Handle to do so (an actor component that can be added)
- In order to call the pre-made functions on the PhysicsHandle component, we'll need to be able to get a pointer to it from our Grabber component in C++
- To do this, we'll need to first include the PhysicsHandleComponent header file in our Grabber's .cpp
- Once we've done that, we can create a pointer to our component by calling the FundComponentByClass template function included on actor classes:

- But the problem here is that if we have not set up a PhysicsHandleComponent, we'll return a nullptr which we need to guard against (as it will crash the editor if we try to call methods on a nullptr)
- (As a side note, if I were coding this as a plugin or extensible component, I'd probably want to add an Assert guard here in order to make sure that a developer using my code would be forced to set the component up correctly)
- To guard against this, all we need to do is run code on the pointer within an if guard which checks if the pointer is null
- That sort of guard would look similar to this:

- DrawDebugSphere()
- Unreal has a lot of debug systems to help developers visualize what's going on behind the scenes in their code - we'll be setting up a sphere visualization
- Eventually, what we want our code to do is use the GrabComponentAtLocationWithRotation() function on the UPhysicsHandleComponent, but we need some informational input for the parameters of the function:
- The component to grab (UPrimitiveComponent)
- The bone name at which to grab it (FName, can be None)
- The location at which to grab it (FVector)
- And the rotation at which the component should be grabbed (FRotator)
- We want to be able to grab the component at the location where we're looking, which we can use the sphere trace we already set up for in the Grab() function (set up previously in the course):
- We can then use the HitResult from that trace in order to get the location at which we should be grabbing the component
- The data members of the HitResult allow us to do this, but there are a few that it could be, which one do we actually want to use? We can use a debug visualizer for this
- We're going to insert two calls to draw debug spheres into our if hit branch and give them two different colors to differentiate them from each other in order to see which locations are received

- And when we play the game and try to grab the gargoyle, the result is:

- As the orange sphere was tracing the ImpactPoint, it looks like that's the vector information we're looking for
- The Location of a HitResult tells us where the location of the sweep that we're casting would be once a hit is calculated - in this case, we're sweeping a 1-meter sphere and its center would be where the green debug sphere is currently - about a half meter away from the gargoyle
- So what we want to use is the ImpactPoint - the location on the component at which the sweep makes contact



