Learning C++ in Unreal Engine: Creating and Using C++ Classes

Julien Hora
tokyopixels
6 min readMay 28, 2024

--

In this article, we’ll dive into creating and using C++ classes in Unreal Engine. Understanding how to define and utilize classes is fundamental to building complex and organized game systems. We’ll cover the basics of creating a new C++ class, using Unreal Engine macros, and adding functionality to your classes.

Creating a New C++ Class in Unreal Engine

Creating a new C++ class in Unreal Engine is straightforward and integrates seamlessly with the Unreal Engine Editor.

  1. Open Your Project: Start by opening your Unreal Engine project in the Unreal Engine Editor.
  2. Create a New Class:
  • In the editor, click the “Tools” button in the menu bar.
  • Select “New C++ Class”.
  • Choose a base class (e.g., Actor or Pawn) depending on what you want your new class to extend.
  • Name your class (e.g., MyCharacter) and click "Create Class".

For this tutorial, we will create a new class LPPCharacter from the Character parent class.

I like to keep code organized, so two things I would like you to keep in mind: choosing Public will make the header of your class publicly available. That is what you would typically go for when you want your class to be available for include from other class implementations. Another thing is that I recommend you keep a decent folder structure that makes things easy to find. For that reason I am creating our new class in Characters/.

Click on Create Classand Unreal Engine will generate the necessary header (.h) and source (.cpp) files for your new class and open them in Rider.

Once you see your class created, don’t be surprised to see an A appended behind LPPCharacter. This is simply Unreal Engine’s prefix system where A stands for Actor.

Understanding UCLASS, UPROPERTY, and UFUNCTION Macros

Unreal Engine uses macros to integrate C++ classes with its reflection system, enabling features like property editing in the editor and Blueprint integration. Features that you can visualize well if you are using Rider.

UCLASS Macro

The UCLASS macro is placed before class definitions to mark them as Unreal Engine classes. This macro enables the class to be recognized by Unreal Engine's reflection system.

UCLASS()
class ALPPCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Constructor
ALPPCharacter();
};

UPROPERTY Macro

The UPROPERTY macro is used to expose class member variables to the Unreal Engine Editor and Blueprints. In this example, the Health variable can be edited anywhere, read and written in Blueprints, and is categorized under "Stats" in the editor.

UPROPERTY()
int Health;

UPROPERTY Macro Specifiers

  • EditAnywhere: Allows the property to be edited in both the Blueprint Editor and the Details panel of an instance placed in a level.
  • EditDefaultsOnly: Restricts editing of the property to the Blueprint Editor only, meaning it cannot be changed per instance in the level.
  • VisibleAnywhere: Makes the property visible in both the Blueprint Editor and the Details panel, but it is read-only.
  • VisibleDefaultsOnly: Makes the property visible only in the Blueprint Editor and read-only.
  • BlueprintReadWrite: Allows the property to be read and modified in Blueprints.
  • BlueprintReadOnly: Allows the property to be read in Blueprints but not modified.
  • Category: Groups the property under a specific category in the editor for better organization.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Stats")
float MaxHealth;

UFUNCTION Macro

The UFUNCTION macro is used to expose class member functions to Blueprints. This makes the function callable within Blueprints, allowing for greater flexibility in combining C++ and Blueprint logic.

UFUNCTION()
void SetHealth(float NewHealth);

UFUNCTION Specifiers

  • BlueprintCallable: Makes the function callable from Blueprints.
  • BlueprintPure: Indicates that the function does not modify the state of the object (no side effects), allowing it to be called without affecting the game state.
  • BlueprintImplementableEvent: Declares a function that can be overridden in Blueprints, but doesn’t require a C++ implementation.
  • BlueprintNativeEvent: Declares a function that can be overridden in Blueprints but provides a base implementation in C++.
  • Category: Groups the function under a specific category in the editor for better organization.
UFUNCTION(BlueprintPure, Category = "Health")
float GetHealth() const;

Class Structure and Access Specifiers

In C++, the structure of a class is divided into different access specifiers: private, protected, and public. These specifiers control the visibility and accessibility of class members, providing a way to encapsulate data and functions.

Public

Members declared under the public specifier are accessible from anywhere in the code. This means other classes and functions can directly access and modify these members. Constructors, destructors, and member functions that need to be called from outside the class are typically placed in the public section.

public:
ALPPCharacter();
UFUNCTION(BlueprintCallable, Category = "Health")
void SetHealth(float NewHealth);

Protected

Members declared under the protected specifier are accessible within the class itself and by derived classes. This allows for controlled access to these members by subclasses while still restricting access from other parts of the program. Protected members are useful for implementing features that should be accessible by derived classes but hidden from the rest of the program.

protected:
UPROPERTY(EditDefaultsOnly, Category = "Health")
float Health;

Private

Members declared under the private specifier are only accessible within the class itself. This encapsulation ensures that the internal state of the class cannot be modified directly from outside the class, promoting data integrity and security. Private members are typically used for internal data and helper functions that should not be exposed to the rest of the program.

private:
int SecretCode;

Example Class Structure

Let’s take a look at this class example:

UCLASS()
class ALPPCharacter : public ACharacter
{
GENERATED_BODY()

private:
// Private member variable
int SecretCode;

protected:
// Protected member variable
UPROPERTY(EditDefaultsOnly, Category = "Health")
float Health;

public:
// Constructor
ALPPCharacter();

// Public member function to get health
UFUNCTION(BlueprintPure, Category = "Health")
float GetHealth() const;

// Public member function to set health
UFUNCTION(BlueprintCallable, Category = "Health")
void SetHealth(float NewHealth);
};

Basic Class Inheritance and Polymorphism

Inheritance and polymorphism are key concepts in object-oriented programming, allowing you to create hierarchical relationships between classes and reuse code effectively.

Class Inheritance

Inheritance allows you to create a new class based on an existing class, inheriting its properties and methods. In this example, ALPPCharacter inherits from ACharacter, gaining access to its functionality while adding new features like SetHealth.

class ALPPCharacter : public ACharacter
{
GENERATED_BODY()

public:
ALPPCharacter();
UFUNCTION(BlueprintCallable, Category = "Health")
void SetHealth(float NewHealth);
};

Polymorphism

Polymorphism allows derived classes to override methods from base classes, providing specific implementations. The override keyword indicates that SetHealth in ALPPCharacter overrides the same method in the base class ACharacter.

void SetHealth(float NewHealth) override;

Adding Functionality to C++ Classes

To make your classes functional, you need to implement their methods and add game-specific logic.

Constructor Implementation

The constructor is used to initialize class members. In this example, Health is set to 100 when an instance of ALPPCharacter is created.

ALPPCharacter::ALPPCharacter()
{
Health = 100;
}

Method Implementation

Implementing class methods involves defining their behavior. The SetHealth method sets the Health variable to NewHealth and logs the updated health value.

void ALPPCharacter::SetHealth(float NewHealth)
{
Health = NewHealth;
UE_LOG(LogTemp, Warning, TEXT("Health now: %d"), Health);
}

Conclusion

Creating and using C++ classes in Unreal Engine is a powerful way to build organized and scalable game systems. By understanding and utilizing Unreal Engine’s macros, inheritance, and polymorphism, you can create complex and interactive game elements.

In the next article, we’ll explore how to integrate C++ code with Blueprints, leveraging the strengths of both systems to create flexible and dynamic gameplay mechanics.

Stay tuned and happy coding!

--

--

Julien Hora
tokyopixels

Founder @ Dreamrunner Labs | Senior Software Engineer | Unreal Engine Generalist