Learned This Week
Overview
A lot of this week's learning centered around physics handles and physics simulations, as well as how those simulations can be accessed, triggered, and manipulated by C++. As I continue to learn more and more, it seems like the most complex thing I've come across is how to get a hold of a pointer or reference to an actor or component in C++.
While I'm glad that most of the rest of the code and syntax comes easily to me, I've certainly had some trouble wrapping my head around how to wrap my code around objects. In blueprint, it's fairly straightforward (if a little complex sometimes) as everything is within the same environment. In C++, you need to think of handling references before compilation in order to be able to make sure your code running on them runs correctly.
I think that supplementing this week's normal learning course content with some documentation regarding how to reference actors helped me get a bit more of a solid grasp on the concept. Next week, I'll be looking into utilizing some flow control functions in C++ such as for loops and range-based loops (which I've learned but have never put to use in an Unreal project) and diving into some of the intricacies of the differences and ad/disadvantages of C++ as compared with blueprint.
Key Points:
- Referencing Actors Within C++
- There are a few ways to get a hold of actors in a level within C++, and they all can be applicable in different situations:
- Spawning the actor directly from C++.
- This allows for direct and easy referencing, but doesn't allow for things like quick and easy manipulation of the object within the world using the editor manipulation tools available.
- Using GetAllActorsOfClass.
- While frowned upon as this can be really costly if you have a lot of instances of the same class in your scene, it will do what you need (though normally it's considered overkill).
- Tracing with anything other than a line (normally a sphere).
- As this generates collisions, the collided object can be referenced from the HitResult of the trace. The problem with this is that it can often hit objects you don't want to reference, so custom trace channels should be used in order to mitigate that effect.
- Running the reference through an instance variable on your class.
- In order to do this, you would create a variable in C++ that you would edit in-world when you place the actor in the scene.
- While more complex, and somewhat of a workaround, it'll do what you need - creating an empty variable on the actor you want to be able to receive the reference and then selecting the actor in the level and setting the variable to whichever actor you want it to reference.
- This only works if both actors are in the level, but it could be set dynamically during runtime as long as the variable is BlueprintReadWrite.
- Physics Handles
- Grabbing vs. Setting Targets
- Handles are handy for getting a hold of objects, but in C++ they function a bit differently than in blueprint - you must first grab the other object, then set its target location on tick.
- Technically, you could just set its target location whenever, but if it's not done on tick, it'll function very oddly and likely won't move in a convincing way.
- In order to let go of the object, you'll need to call a release on the handle.
- Waking Rigid Bodies
- Physics simulations go to sleep after they are not interacted with/on for a while in order to preserve memory and assist the efficiency of the application.
- Unfortunately, this means that physics sims are not always ready to be acted upon whenever you need them to be.
- In order to get a sim ready to move/be used, you need to wake it first - this should be done whenever you're grabbing or releasing something, as you want to avoid the object sticking where it is in the air when you release it if the player hasn't moved for a while and the object went to sleep.
- Out Parameters
- Just like in generic C++, complex functions that need to return multiple values should use out parameters in order to do so.
- As C++ is statically typed, its functions can only handle one return value (which you use to specify the start of a function).
- In order to create an out parameter, we use variables passed as reference, making sure to set them as what we want them to be inside the function. That way, the variable is taken by the function, edited, and then the outer code can still access that edited value because it was passed by reference.
- As long as a function is a UFUCNTION that can be called in a blueprint in some way, out parameters can also be identified by the engine and will be listed on nodes calling the function.
- UPARAM macros can be used to tell the editor how to display certain output pins in the blueprint node that is created (i.e. whether they should show up as reference inputs, what their display name should be, etc).
Nitty-Gritty
- Programming With C++ UE Documentation - Jump to Top
- Referencing Actors
- It's far more difficult to reference actors directly in C++ as the code is compiled before it even gets a look at what may be in the world
- However, there are a few ways in order to do so:
- Spawn the actor from code
- This allows for direct and easy referencing, but doesn't allow for things like quick and easy manipulation of the object within the world
- Using GetAllActorsOfClass
- While frowned upon as this can be really costly if you have a lot of instances of the same class in your scene, it will do what you need (though normally it's considered overkill)
- Tracing with anything other than a line (normally a sphere)
- As this generates collisions, the collided object can be referenced from the HitResult of the trace
- Running the reference through an instance variable on your class
- While more complex, and somewhat of a workaround, it'll do what you need - creating an empty variable on the actor you want to be able to receive the reference and then selecting the actor in the level and setting the variable to whichever actor you want it to reference
- This only works if both actors are in the level, but it could be set dynamically during runtime as long as the variable is BlueprintReadWrite
- UMG in C++
- In order to access UMG code in C++, it needs to be added along with its dependencies to the build.cs file of the project (which defines how the Unreal Engine Header Tool builds the project)
- While it's not practical to create the layout of any UMG blueprints in C++, you can derive widgets from parent C++ classes in order to build in functionality the same way you can with other derived blueprint classes
- Unreal 5: Learn C++ GameDev.tv Course - Jump to Top
- Grabbing with Physics Handle
- In order to grab components, we need to both grab it and set its target location in order to make it move around with the player
- Because we need to get the physics handle of the owner class of this scene component, we'll need to guard against the possibility of the pointer being null in our grab function with an early return if guard like so:

- And we can use the sphere trace we've set up earlier in order to grab the component using the GrabComponentAtLocationWithRotation function, using the ImpactPoint of the HitResult (that we determined last week was the data we needed from the trace) like so:

- Now that we've grabbed the component, we need to update its location relative to the player on every tick by using the SetTargetLocationAndRotation function on the PhysicsHandle of the player
- The function takes in a hold distance which we would like to be able to edit on the component, so we can make it into a UPROPERTY specified as EditAnywhere
- We can then multiply that with the player's forward vector and add it to the scene component location in order to get a target location to use for the grabbed component:

- And once we've set that information up, we can use it in the function to determine where the grabbed component should be:

- Now we can grab our gargoyle! Additionally, I can use the information I learned earlier to force any developer using the Grabber scene component to have a PhysicsHandle component on the player by using an Assert:
- Additionally, I can edit the properties of the PhysicsHandle to make the grabbed component react faster - this is the result:
- Waking Physics Objects
- Actors with physics enabled "go to sleep" when they are not actively being engaged with/operated on for performance's sake, so we need to "wake" the gargoyle when it is hit with a sphere trace in order to get it ready for the player to grab
- UPrimitiveComponents have a WakeAllRigidBodies method which we can call (it will only wake all the rigid bodies held on the component, so we don't have to worry about it waking everything in the scene)
- We can also extend the Release function of our grabber component to wake the simulation before it is released, as if the component stays in the same place for a while it may go to sleep and releasing it would do nothing:

- We should also guard the target setting in the tick of the component against setting any target location/rotation unless the physics handle is grabbing a component:

- Returning Out Parameters
- Because C++ is statically typed, functions can only truly have one return value - however, a way of getting around this is returning out parameters
- Out parameters are parameters of the function that are references, meaning that they can then be accessed outside the function as well
- Unreal parses out parameters as additional return values in blueprint as long as the parameters are references and the function is a UFUNCTION
- You can identify what the pin should look like/display as by using a UPARAM macro with the DisplayName specifier
- Because out parameters are references, they need to be handled carefully, but are inherently designed to change inside the function and be returned as a different value
- Overlap Events
- We want to be able to detect when the player places the gargoyle into the crypt wall in order to open that wall - we can use a collision box for that, which we're going to add as a component to the wall in C++
- In order to do so, we'll need to create a new scene component that inherits from BoxComponent in C++ which we can then add to our blueprint in the editor
- Thankfully, all we really need to do is create our own custom inherited version of the BoxComponent as it has most of the functionality we'll need - we can edit its location, box extents, collision presets, and bind events from it
- Balancing Blueprint and C++ in Game Development Course - Jump to Top
- Introduction to the Course
- This course is aimed at displaying the benefits of balancing both scripting languages as well as demonstrating the upsides and downsides of both/when to use each
- Student Prerequisites
- Have completed the "Converting Blueprints to C++" course ✔
- Familiar with Unreal, Blueprints, and basic C++ ✔
- The course will help to identify where each language excels
- This knowledge will be applied to some of the existing Unreal code (in content examples and templates) to optimize and improve it
- Project Setup
- The project for this course will stem from the Unreal Twin Stick Shooter template
- I'll be working in Unreal 5, but the course was recorded in 4.25, so I'll have some adjustments to make through the process (also, the template is from 4.27 and I've converted it to 5, so I might hit some roadblocks down the line that I'll need to adapt to)
- Because the project we've created is C++ only, there are no derived blueprint classes in the template
- Gameplay Variables
- Our first step to understand the project template is to dive in and take a look at some of the C++ classes and how they are set up, and identify how variables are utilized by the game
- We'll also take a look at how some of those properties may be better utilized in blueprint
- Currently, all of the variables that control our pawn are defined in C++ but can be edited anywhere in the editor and are also able to be edited from blueprints:

- There are a few problems with this:
- By default, if you want to just use the C++ version of the class, the pawn's variables can only be edited in the Details panel (so they won't transfer across maps)
- Each instance of the class has its own variables, none of which are shared across all instances
- We can't use the PlayerStart actor if we want to set any variables to non-default values, as we'd need an instance of the class in the world
- What comes along with this as well is the issue that we need to set the specific instance of the pawn to be auto possessed, otherwise Unreal will spawn a new instance of the class due to the GameMode information we're giving it - this is generally frowned upon
- Currently, the variables themselves are set up in a helpful way, but the project isn't using blueprint classes derived from the C++ class, which means that the project itself is not set up in a helpful way
- As we go along throughout the course, we'll be creating derived classes from the C++ code and improving their usage within the project