Les bases du C++ UE4

Apprenez les bases du C++ sur Unreal Engine 4 !

Les bases du C++ UE4

Apprenez les bases du C++ sur Unreal Engine 4 !

Crée le 3 avr. 2021

Prérequis

Avant de vouloir commencer à programmer en C++ avec Unreal Engine 4, il est fortement recommandé d'avoir au moins des bases en C++, ou en autre langages simillaire ainsi que des bases sur Unreal Engine 4. Je déconseille fortement de commencer UE4 en C++.

Introduction

UE4 est un moteur de jeu proposant deux langages pour programmer vos jeux: Le Blueprint et le C++, contrairement à ce qu'on peut lire sur Internet, il n'y a pas de vraie concurrence entre les deux langages, les deux sont a utilisés ensemble pour profiter un maximum de la puissance du moteur.

Le Système d'UObject

Dans Unreal Engine 4, tous les Objets de base du moteur héritent de la classe UObject. En héritant de cette classe, vous allez pouvoir utiliser des fonctionalités très pratique de ce système d'UObject comme la reflection, la serialization, etc

Garbage Collector

Tous les UObject dans UE4 sont soumis à un Garbage Collector comme en C# ou en Java, son rôle est de détruire les objets qui ne sont plus utilisés. Le GC d'UE est un garbage collector mark-and-sweep: tous les objets qui ne sont plus referénces ou qui ont explicitement été marqué pour la destruction vont être détruit lors du prochain cycle du GC. Le Garbage Collector fonctionne un peu comme un arbre, vous trouvez tout en bas les objets dit dans le "root set", ces objets ne sont jamais détruits tant que vous ne demandez pas explicitement au moteur de le faire, et le GC va traverser recursivement les références aux UObject depuis le root set jusqu'à arrivé au bout.

Le GC remonte les reférences aux objets grâce au système de reflection que nous allons voir juste après.

Grâce au GC, la gestion de la mémoire dans UE4 est assez simple, et est très similaire à ce qu'on peut trouver en C#/Java.

Pour plus d'infos: Doc UE4

Reflection (UFUNCTION, UPROPERTY, UCLASS, ...)

La reflection permet à un programme d'analyser sa propre structure (ses classes, ses variables, ses fonctions). Elle est grandement utilisé dans Unreal Engine pour faciliter la vie, elle permet par exemple de pouvoir exposer ses variables dans l'éditeur pour les modifiers, ou aussi exposer une fonction C++ en Blueprint.

Le système de reflection d'Unreal fonctionne grâce à un outil externe appelé l'Unreal Header Tool. Son job est de lire vos headers et de générer du code qui va informer le système de reflection du moteur de vos types. L'Unreal Header Tool utilise les macros pour pouvoir identifier les types.

Vous pouvez rendre accessible vos types avec les macros suivantes

  • UCLASS : Pour les UObject
  • USTRUCT : Pour les structures
  • UENUM: Pour les enumérations

Attention: Il y a une certaine convention à respecté pour les types reflectés. Les structures doivent commencés par un F, les énumérations par un E et les UObject par un U.

Voici un exemple d'utilisation de UCLASS:

UCLASS()
class UMonObject : public UObject

Pour pouvoir rendre accessible au moteur vos variables et fonctions, vous pouvez utilisez les macros UFUNCTION et UPROPERTY:


UCLASS()
class UMonObject : public UObject
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable)
    void MaFonction();
    
    UPROPERTY(BlueprintReadWrite)
    float MonFloat;
};

Comme vous pouvez le voir, nous pouvons ajouter des informations supplémentaires à l'intérieur des paranthèses des macros, cela permet par exemple de dire au moteur de rendre modifiable une variable aux Blueprints (BlueprintReadWrite) ou de pouvoir appeler une fonction en Blueprint (BlueprintCallable)


Faites attention aux types que vous utilisez. Le système de reflection d'Unreal supporte un nombre limité de types. Vous ne pouvez pas exposez de type complexes (exemple templates, sauf cas spéciaux du moteur). En exposant une fonction ou une variable aux Blueprints, vous reduisez aussi le nombre de types que vous pouvez reflect (exemple les types non-signés ne sont pas supportés en Blueprint mais le système de reflection le supporte).


Les listes ne sont pas exhaustives, il y a des specifiers cachés. Vous pouvez les chercher à travers le code source du moteur.

Le système de reflection d'Unreal est donc très utile et est la base de beaucoup d'autre systèmes du moteur tel que le Garbage Collector:

Lorsque le GC execute son cycle, il va traverser la hiérarchie d'UObject, en passant aussi par les pointeurs d'UObject qui sont en UPROPERTY:

UPROPERTY()
UMonObject* MonObjet

Cela permet de rendre la gestion de la mémoire automatique, sans que vous ayez besoin de dire au GC que vous avez tel reférence à tel objet.

Le système de module

Tout Unreal Engine 4 est fait de modules.

Un module est une librarie contenant du code qui permet d'être utilisé par d'autre modules. Cela permet d'avoir du code facilement réutilisable sur d'autre projets et accélerer les temps de compilations et la gestion général de votre code.

Pour fonctionner, un module a besoin de deux choses:

  • Un fichier NomDuModule.Build.cs
  • Un fichier .cpp

Le fichier Build.cs agit un peu comme un Makefile, c'est ici que vous allez renseignez les options de compilations/de linker de votre module comme les include paths, les libs, ou aussi les autres modules à utiliser.

using UnrealBuildTool;

public class MonModule : ModuleRules
{
    public MonModule(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] {
            "Core",
            "CoreUObject",
            "Engine",
            });
    }
}

Dans le code donné ci-dessus: nous indiquons à l'UnrealBuildTool (le système qui gère et compile vos modules) que nous voulons utiliser le module Core, CoreUObject et Engine (des modules de base du moteur qui sont quasiment toujours utilisés). Vous noterez peut-être le préfix "Public" à la variable PublicDependencyModuleNames, c'est parce-que les modules ont deux portés: une porté publique, qui sera visible/herité par les modules utilisant votre module, et la porté privée qui elle est privée.

L'interêt de séparer le code publique et privé et de pouvoir encapsuler correctement votre code et d'éviter les potentiels problèmes qu'une mauvaise encapsulation peut apporter. Vous pouvez retrouvez cette hiérarchie de Public/Privée partout dans le code source du moteur.

Après avoir ajouter votre fichier Build.cs, il vous faut aussi un minimum un fichier .cpp avec dedans:

#include "ModuleManager.h"

IMPLEMENT_MODULE(FDefaultModuleImpl, MonModule);

La macro IMPLEMENT_MODULE ajoute du code obligatoire qui permet à UE4 de charger/décharger votre module correctement, si vous l'oubliez vous risquez d'avoir des bugs.

Notez que lorsque vous voulez rendre visible/exporter un symbole (fonction, variable, type) pour qu'il soit utilisé par d'autre modules, il faut ajouter la macro MONMODULE_API:

class MONMODULE_API UMonObject : public UObject

Pour ceux qui se demandent pourquoi, c'est parce-que les modules sont compilés en tant que librarie dynamique ET statique suivant le contexte ou ils sont builds (exemple le moteur va compiler tous ses modules en lib statique pour faire la release finale de votre jeu, mais pour la version dans l'éditeur il fera une librarie dynamique car plus facile à gérer)

Conclusion

Et voilà ! Comme vous pouvez le voir, le C++ UE4 est assez différent du C++ normal et se rapproche plus des langages type C#/java avec notamment le Garbage Collector (attention, ce que je dis est uniquement valables lorsque vous travaillez avec des UObject ! Lorsque vous quittez le "monde" des UObject, ça redevient du C++ pur!)

Maintenant, vous pouvez tentez de crée des acteurs, des components, et de vous amuser avec l'api C++ UE! Elle est très similaire au Blueprint donc vous pouvez facilement retranscrire votre code BP en C++, vous pouvez aussi tenter de nouvelles choses grâce aux possibilités qu'offre le C++ comme le multi-threading, link des libraries externes etc...

Je conseille fortement de lire la doc UE4 qui est plutôt pas mal, et aussi un autre conseil que je trouve assez important, c'est d'explorer le code du moteur. Ca peut paraître impensable mais savoir se retrouver dans le code du moteur/savoir en gros comment certains systèmes fonctionnent vous facilite énormément la vie lorsque vous développez avec UE4, et cela vous permet en plus d'être plus autonome.

Envie de lire la suite de ce tutoriel ?

Connecte-toi dès maintenant, et accède entièrement à tous les tutoriels de GCA !

#C++

#Unreal Engine

#dev

Écrit par Zino#4979

7

Sommaire