quinta-feira, 29 de outubro de 2020

The UFUNCTION() macro

The UFUNCTION() macro is used to expose C++ functions to the Blueprint editor. It is possible to create C++ functions that will be called in the Blueprints editor. It is also possible to call in C++, functions that have been implemented in Blueprints.

A Blueprint can represent a C++ function in three different ways. Events are implemented as C++ functions that do not return a value, that is, their return type is void:


A Blueprint function with an execution pin is implemented as a C++ function that is not marked as BlueprintPure: 


If the C++ function is marked with BlueprintPure then it is represented as a pure function. A pure function has no execution pin, cannot modify instance data and can be used in expressions. It is widely used in Get functions.


Now let's talk about the function specifiers. First, every UFUNCTION() must have the Category specifier for the function to display correctly in a Blueprint's context menu.

For a UFUNCTION() to be used in a Blueprint's Event Graph, it must have one of these two specifiers:

  • BlueprintCallable: It will appear as a Blueprint function with an execution pin.
  • BlueprintPure: It will appear as a pure function.


It is possible to define a C++ function that will be implemented in Blueprint. To do this, use one of these two specifiers in the UFUNCTION():

  • BlueprintImplementableEvent: The function is implemented only in Blueprint. If a C++ implementation is found, it will generate a compilation error.
  • BlueprintNativeEvent: Allows the implementation of a C++ version of the function that will be used if the Blueprint version is not implemented. The C++ version must have the function name plus the suffix _Implementation.

If the return type of the C++ function is void, it will be implemented as an Event in Blueprints. If the C++ function returns a value, then it will be implemented as a Blueprint function.
 
Let's see all of these specifiers in action in the example usage.


Example usage:

Create a C++ class with the name TestUFunction using the Actor class as the parent class. In the TestUFunction.h file, add the declarations of four functions below the Tick() function, as shown in this code:

...

public:	
  // Called every frame
  virtual void Tick(float DeltaTime) override;
	
  UFUNCTION(BlueprintCallable, Category = Tutorial)
  int32 WelcomeMsg(FString PlayerName);
	
  UFUNCTION(BlueprintPure, Category = Tutorial)
  float CalculateDamage(float PowerAttack, float DefenseValue);
	
  UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = Tutorial)
  void TreasureFound();
	
  UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Tutorial)
  FString NativeExample();
};

In the TestUFunction.cpp file, add the implementation of the three functions shown below: 

...

int32 ATestUFunction::WelcomeMsg(FString PlayerName)
{
  FString Message = FString::Printf(TEXT("Welcome %s."), *PlayerName);
  
  if(GEngine)
  {
    GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, Message);
  }
  
  //Returns the number of characters in PlayerName
  return PlayerName.Len();
}

float ATestUFunction::CalculateDamage(float PowerAttack, float DefenseValue)
{
  return (PowerAttack * 10) / DefenseValue;
}

FString ATestUFunction::NativeExample_Implementation()
{
  return TEXT("NativeExample: C++ version");
}

Note that the TreasureFound() function has not been implemented. It is because it will be implemented only in Blueprint due to the BlueprintImplementableEvent specifier. The NativeExample() function has the BlueprintNativeEvent specifier that allows it to be implemented in C++ and Blueprint. The C++ version was implemented with the name NativeExample_Implementation().

Compile the C++ code. Create a Blueprint based on the TestUFunction C++ class. To do this, right-click on the TestUFunction class and choose the option Create Blueprint class based on TestUFunction.

Right-click on the Blueprint Event Graph and see that the four functions declared in C++ are being listed in the Tutorial category as shown in the image below. They appear in this listing because they have the BlueprintCallable or BlueprintPure specifier.


We will call the four functions in the Blueprint's BeginPlay Event. Add the functions to the Event Graph as shown in the image below.

Click to enlarge

Before testing, we need to implement the TreasureFound() function in Blueprint. On the My Blueprint tab, click on the Override button next to Functions. Notice that the Treasure Found and Native Example functions appear in the listing. Click on Treasure Found.


The return type of the TreasureFound() function is void, so it is implemented as an Event in Blueprint. Add a Print String function with a message:


Compile the Blueprint and add an instance at the level. Start the game and see the messages on the screen. Each line represents the execution of one of the functions.


As a Blueprint version of the NativeExample() function was not implemented, the C++ version was used. To complete this example, we will implement the Blueprint version of NativeExample().

On the My Blueprint tab, click the Override button next to Functions and select Native Example. It is implemented as a function because it has a return value. See the Parent: Native Example node in the image. This node will execute the C++ version of NativeExample(). This allows you to add functionality in Blueprint for this function. But if you want the Blueprint version to replace the C++ version, just remove this node. 


For our example, remove the Parent: Native Example node and write a message in the parameter Return Value.


Compile the Blueprint and start the game. Note that only the Blueprint version of NativeExample() was called. 


Table of Contents C++