Skip to main content

Week 5 - Components & Delegates

Learned This Week

Overview

While this week was primarily focused on reading documentation from the Epic Games Unreal docs which repeated information I already learned, it was helpful to read through it to provide additional cementing in my noggin.

Additionally, I learned about some helpful ways to integrate blueprint code with C++ code and ways in which documentation and notes can be added directly to C++ code and display in blueprints. As I build on the Unreal implementations of certain code principles in C++, I'm glad I structured this study in such a way that I learned the base concepts and inner workings of C++ outside of an Unreal context first, as now when I'm working through Unreal code, it's fun to see my mind making connections and identifying the concepts & syntax in play.

I got to peek a little into the inner workings of Unreal classes this week while looking at component creation and attachment with C++ code. I've created inheritance trees and complex actor-component relationships previously in blueprints, but it was really interesting to see those concepts applied in a C++ environment, because they just make sense! I can see the potential of classes integrated between C++ and blueprint, especially when inherited components come into play. Cool stuff!

Key Points:

  • Exposing Variables & Functions to Blueprint
    • UPROPERTY
      • Unreal macro which permits C++ variables to be used within Unreal.
      • Adding additional keywords allows the variable to be accessed, read from, and written to by the engine and by blueprints - more information is available on Week 4's post.
    • UFUNCTION
      • Unreal macro which permits C++ functions to be used within Unreal.
      • Adding additional keywords allows the function to be called to and from by blueprints - more information is available on Week 4's post.
      • When adding pins, blueprint display names can be specified by using the UPARAM macro.
  • Component Creation & Attachment in C++ 
    • Components are Unreal's implementation of the OOP concept of Composition - components can be attached to Actors and placed inside the level, allowing for the easy re-use of code along with simple garbage collection (when the Actor is created/destroyed, so is the component).
    • In C++, components can be declared and defined but they can also be attached. By creating SubObjects in the constructor of the class, components can be attached to actors on construction.
    • Root components can also be defined in C++.
    • If the class is blueprintable, this allows for very easy re-use of code and implementation of inheritance with derived blueprint classes.
  • Delegates - Subscribing & Broadcasting
    • Similar to the Event Dispatcher system in blueprints, delegates can be created in C++ in order to call functions between locations in-engine.
    • Delegates have a funky declaration setup, and can be called in blueprints (as long as they are dynamic). See the Declaring Delegates for Blueprint section below for more in-depth information on delegate declaration.
    • Subscriptions and broadcasts are controlled by methods called on declared delegates in C++.

Nitty-Gritty

Jump to:
  • Programming With C++ UE Documentation - Jump to Top
    • Gameplay Artchitecture
      • Each class defines a template for a new Actor or Object (AActor or UObject)
      • Classes contain functions and properties
        • They can also contain data structs
      • Interfaces allow specific behavior to be implemented by different classes
      • Class prefixes:
        • A - extended from the base class of any spawnable gameplay objects (can be spawned into the world)
        • U - extended from the base class of any gameplay object, cannot be directly instanced into the world but can be attached to an actor or belong to an actor (components)
      • Each class has its own default constructor defined by the engine which can have functionality added to it
        • Sub-objects and components can be added to classes in the constructor
    • Exposing Gameplay Elements to Blueprints
      • To create blueprints that extend from a class, the Blueprintable keyword must be added to the UCLASS() macro (though the Blueprintable keyword is inherited, so it may already have been defined as blueprintable depending on the parent class of the class you're creating)
      • UPROPERTY & UFUNCTION are used to expose or call C++ variables or functions from the blueprint graph (see Week 4's post for more information)
      • When passing parameters passed by reference in C++, Unreal will also display them as output parameters on the function call node in blueprints as it will assume you want to use it as such. To make it an input parameter, the UPARAM macro is used inside of the function signature like so: void EatCookies (UPARAM(ref) int& NumberOfCookies)
      • The UPARAM macro can also be used to change the display name of a pin by specifying it like so: UPARAM(DisplayName="X (Roll)") float Roll
      • BlueprintImplementatbleEvent & BlueprintNativeEvent (see Week 4's post for more information)
    • Asserts
      • This system is one way of helping to detect and diagnose unexpected/invalid runtime conditions during development (something that the code requires but that would be inefficient to manually check by combing through logs every time the code is run)
      • Easy way of assisting developers to discover gameplay bugs before the code breaks
      • Slots into three different types: check, verify, and ensure
        • Check
          • Does not run in shipping builds by default, only in development builds and in the editor
          • Will halt execution and output a message if their condition is met
          • Certain checks exist for things like evaluating a boolean expression, if a line in the code is reached, if a line is hit more than once, etc
        • Verify
          • Also operate in shipping builds and run the code associated rather than just checking an output value
          • Halt execution if their condition/expression is false
        • Ensure
          • Similar to Verify, but do not halt execution
          • Meant to be used with non-fatal errors
          • Sends information to the crash reporter but does not forcibly halt execution
    • UFUNCTIONs
      • Defining a function as a UFUNCTION will make Unreal recognize it as a part of the code that should be visible to Unreal
      • Delegate functions can be defined in C++ (similar to the blueprint delegate system)
      • Timers can be set in C++ which function similarly to the blueprint timer system
      • More information can be found on Week 4's post
    • Interfaces
      • Helpful when a set of classes (either related or unrelated) need to implement a common set of functions
      • When implementing the interface, multiple inheritance is used to inherit from the class you're creating's base class as well as the interface class
  • Unreal Converting Blueprint to C++ Course - Jump to Top
    • Example 1: CompleteQuest
      • We'll be migrating this function from blueprint to C++ - the original function looks like this:

      • We've already migrated the QuestInfo struct to C++, so we can begin to move the function itself as well
      • The blueprint function GetQuestIndex is called in the function - we could migrate that to C++ as well, but for the sake of experience and demonstration we're going to have it as a BlueprintImplementableEvent instead which gets called in C++ 
      • An event dispatcher (delegate) is called at the end which we'll get to later
        • As it's BlueprintNative and not BlueprintImplementable, we can call the parent function in C++ from the blueprint side of things later, which will allow us to keep some of the functionality in blueprint such as this event dispatcher and migrate the rest to C++
      • The C++ version of the code looks like this:

      • And its blueprint implementation boils down to the following:

      • And it's now migrated!
    • Creating Components in C++
      • Components can be reused on multiple actors
      • We're going to migrate the ParticleSystem component found on the BP_QuestMarker blueprint over to C++
      • We want to create and add the component in the C++ constructor for the actor, so we need to make a C++ parent class for the BP_QuestMarker and reassign the parent class of the blueprint to it
      • We can then define a UPROPERTY on the newly created parent class to hold the constructed component variable (we don't need to set a default value because we'll do that in the constructor) like so:

      • And we use the CreateDefaultSubobject<type>(FName) function to create a particle system component named ParticleSystem like so:

      • Now that it's been created, we need to assign it to the variable we created earlier as well as set it to be the RootComponent of the actor it's being added to like so:

      • While that's helpful, it's not exactly what we want to to - we should be setting a SceneComponent to be the RootComponent of the actor which we can do like so:
        • in the header file, add a SceneComponent variable to use later as a root component

        • and then add another subobject and assign it to the RootComponent variable, then set that as the RootComponent for the actor - we can also add the ParticleSystem to it using SetupAttachment

        • With the resulting actor component structure being generated in blueprint as follows:

    • Example 2: RefreshVisiblity
      • We'll be migrating the RefreshVisibility function of the BP_QuestMarker into C++, the original function looks like this:

      • It checks the current quest's progress and sets its own visibility accordingly
      • In order to migrate this function, we need to move its dependencies (variables QuestName and ShowAtProgress along with functions GetQuestManager and IsActiveQuest)
      • The variables are set for each instance of a QuestMarker in the world, so we set them up as EditAnywhere and BlueprintReadOnly like so:

      • Once the variables are implemented, we need to copy across the data from each instance of the blueprint in the world manually
      • The functions are fairly straightforward to map over, and most just require a quick note in C++ about them being BlueprintImplementable in order to re-use the code from other locations
      • The RefreshVisibility function ends up looking like this:

      • And we don't need to do any further migrating, as the ParticleSystem is already in C++ from our earlier migrations!
    • Declaring Delegates for Blueprint
      • Delegates in Unreal can be dynamic or non-dynamic
        • Dynamic C++ delegates can be serialized, found by name, and can be used in blueprints - unfortunately, it is less efficient as a result
      • Delegates can be Multicast or Singlecast
        • Multicast delegates can have multiple listeners (so multiple events can fire at the same time in different locations
          • They also have no return value
      • Delegates and events are different in Unreal - events can have multiple listeners and have no return value, but you cannot broadcast outside the class with events (this is how delegates differ)
      • Delegates in C++ are declared using the DECLARE_DYNAMIC_CASTTYPE_DELEGATE_RetValue_ParamNumber structure
        • An example would be DECLARE_MULTICAST_DELEGATE_TwoParams, which would declare a non-dynamic multicast delegate without any return value and with two parameters
      • Events in C++ are declared using the DECLARE_EVENT_ParamNumber structure
        • An example would be DECLARE_EVENT_SixParams, which would declare an event with six parameters
      • At the moment, there's an Event Dispatcher in the QuestManager blueprint called CompletedQuest - we're going to create the equivalent of that in C++

      • The current Event Dispatcher is a multicast because it has multiple listeners and no return type, it's dynamic because it needs to be called in blueprints, and it has one parameter, so we can declare a delegate in C++ that matches that signature

      • Once declared, we need to make a public UPROPERTY that is BlueprintAssignable and Callable in order to broadcast to the delegate from blueprints

    • Broadcasting and Subscribe in C++
      • Binding/subscribing events, calling dispatchers/delegates, and brodcasting to events are all possible in C++
      • We're going to take a look at broadcasting and calling a delegate/dispatcher first
      • We want to be able to call the "Completed Quest" event dispatcher in C++ in the CompleteQuest function, in order to do that we can use the dynamic delegate we already created in C++ calling the .Broadcast() method along with the parameter we specified, like so:

      • Subscribing to events is a little more complicated - in blueprint, the current subscription looks like this:

      • We need to be able to bind the event, so we'll need to create a function in C++ in order to do so:

      • And in order to subscribe, we need to call a bind in the BeginPlay of the class just like it is being done in blueprints - this is called adding in C++, and it is called as a method on the delegate which we have already created (the addition needs to be dynamic because the delegate is dynamic, for non-dynamic delegates the method is just called .Add):

      • As we've migrated the entirety of these broadcasts and subscriptions in to C++, the redundant blueprint code can be deleted - and that's it!