Avant de commencer
Assurez-vous d'avoir un Projet Unreal Engine en C++.
Cette page est la suite directe de la rubrique Ajouter une caméra à la première personne, un maillage et une animation. Cependant, vous pouvez la suivre indépendamment du reste de la série de tutoriels Coder un jeu d'aventure à la première personne si vous souhaitez simplement en apprendre plus sur le jeu piloté par les données.
Organisation des données dans les jeux
La façon d'organiser et de représenter les données est un aspect important de la conception de jeu. Les éléments interactifs peuvent avoir des qualités très différentes et exister dans le jeu ou simplement en tant que données. Vous pouvez représenter ces données en créant des classes et des blueprints distincts pour chaque type d'élément, ce qui permet de distribuer et de communiquer les données entre différents acteurs. Toutefois, cela devient moins efficace à mesure que votre jeu et la quantité de données stockées augmentent.
Le gameplay piloté par les données est une meilleure approche. Au lieu de coder en dur, vous organiserez les données dans un emplacement centralisé géré par des systèmes dans votre jeu. Le gameplay piloté par les données vous permet de charger ce dont vous avez besoin, quand vous en avez besoin. Par exemple, de nombreux jeux utilisent des tableurs pour organiser le dialogue, car il est plus facile pour un système d'extraire une ligne de dialogue spécifique au lieu d'en stocker des centaines dans chaque personnage.
Dans cette section, vous apprendrez à utiliser cette méthodologie pour commencer à créer des objets personnalisés.
Éléments de gameplay pilotés par les données
Avant de commencer à créer des éléments de base, il est important de réfléchir à ce qui définit un "élément". Étant donné qu'un objet peut être n'importe quel élément avec lequel le joueur interagit, il doit disposer d'un ensemble minimal de propriétés valides pour tout type d'objet. Vous devrez configurer cela dans une structure de données d'élément. Vous devriez également avoir un endroit centralisé pour organiser et afficher ces données d'élément. Pour cela, vous utiliserez une ressource de table de données.
Votre structure de données d'élément fait office de modèle qui définit le type de données de votre élément, puis votre table de données et vos ressources de données stockent les entrées de données réelles en fonction de votre structure.
Le diagramme ci-dessous montre les quatre éléments de gameplay pilotés par les données que vous allez créer dans cette partie du tutoriel. Quand vous aurez fini de paramétrer les quatre éléments, vous pourrez revenir sur ce diagramme plus en détail pour résumer ce que vous avez créé.
Commencez par créer deux fichiers pour définir vos données d'élément :
ItemData.h: Conteneur pour la déclaration de struct de données d'élément (FItemData).ItemDefinition.h: Une classe qui hérite deUDataAssetpour que vos données d'élément puissent être utilisées dans l'Unreal Editor.
La structure de données d'élément n'hérite pas de UObject et ne peut pas être instanciée dans un niveau. Vous avez donc également besoin de la classe Ressource de données à utiliser et référencer dans l'éditeur.
Ensuite, dans l'Unreal Editor, vous allez créer une table de données et une instance de ressource de données en fonction d'une ItemDefinition.
Définir vos données d'élément
La structure de données d'élément définit les données ou propriétés que doit posséder chaque élément de la table de données, et qui fonctionne comme les colonnes du tableau.
Votre structure de données d'élément aura les propriétés suivantes :
identifiant : un nom unique pour l'élément, utile lors du référencement des lignes du tableau.
Type d'élément : le type de cet élément (dans ce cas, vous définirez les types d'outils et d'objets à usage unique).
Texte d'élément : données textuelles sur l'élément, incluant son nom et sa description.
Base d'élément : la ressource de données ItemDefinition associée à cet élément.
Si vous souhaitez créer vos propres champs de tableau (colonne), n'oubliez pas que les champs de table de données peuvent être de n'importe quel type compatible avec UPROPERTY().
Créer un conteneur de fichier d'en-tête pour la structure
Configurez un nouveau dossier et un nouveau fichier d'en-tête (.h) pour stocker votre définition de structure de données d'élément.
Vous pouvez créer la structure FItemData dans ItemDefinition.h, mais placer la structure dans un fichier séparé aide à organiser vos éléments de données et permet de les réutiliser.
Pour configurer un fichier d'en-tête comme conteneur pour votre structure de données d'élément, suivez les étapes ci-dessous :
Dans l'Unreal Editor, allez sur Tools > New C++ Class.
Dans la fenêtre Choose Parent Class, sélectionnez la classe parente None, puis cliquez sur Next.
À côté de Path, cliquez sur l'icône de dossier. Dans votre dossier
Source/[ProjectName],créez un dossier nomméDatapour y stocker cette classe.Nommez la classe
ItemDataet cliquez sur Create Class.Si l'Unreal Engine n'ouvre pas automatiquement vos fichiers de classe, ouvrez votre projet et
ItemData.hdans Visual Studio.Supprimez tout le texte dans
ItemData.cpppuis enregistrez et fermez le fichier. Vous ne l'utiliserez pas.Dans
ItemData.h, on supprime tout ce qui se trouve sous la ligne#include “CoreMinimal.h”L'en-tête
CoreMinimal.hinclut des types basiques commeFNameetFString, ainsi que d'autres types pour définir vos données.En haut de
ItemData.h, ajoutez#pragma onceainsi que les déclarations suivantes :#include “Engine/DataTable.h”: Nécessaire pour que votre structure hérite deFTableRowBase.#include “ItemData.generated.h”: Requis pour l'outil d'en-tête de l'Unreal. Assurez-vous que cette instruction #include vient en dernier pour que votre code compile correctement.
C++#pragma once #include "CoreMinimal.h" #include "Engine/DataTable.h" #include "ItemData.generated.h"Ajouter une déclaration directe pour une classe appelée
UItemDefinition. Il s'agit de la ressource de données avec laquelle vous pourrez travailler dans l'éditeur.C++class UItemDefinition;
Définir les propriétés des éléments
Il n'existe aucun type de variable pour les données de texte et de type d'élément, donc vous devrez les définir.
Pour définir une enum de type d'élément, suivez les étapes ci-dessous :
Créer une nouvelle
classe enumqui répertorie tous les types d'éléments possibles. Dans ce tutoriel, vous allez créer des outils et des objets à usage unique.Nommez la classe enum
EItemTypeet donnez-lui le typeuint8. Ajouter la macroUENUM()au-dessus pour déclarer cette enum à l'outil d'en-tête de l'Unreal.C++// Defines the type of the item. UENUM() enum class EItemType : uint8 { };Ajouter deux valeurs personnalisées à cette enum :
Tool, en ajoutant une macroUMETA()avecDisplayName = “Tool”Consumable, en ajoutant une macroUMETA()avecDisplayName = “Consumable”
C++// Defines the type of the item. UENUM() enum class EItemType : uint8 { Tool UMETA(DisplayName = "Tool"), Consumable UMETA(DisplayName = "Consumable") };
Ces types d'éléments sont personnalisés et ne sont pas intégrés à l'Unreal Engine. Une fois que vous aurez appris les bases, vous pourrez créer tout ce que vous voulez ! (Peut-être un QuestItem, Currency ou Disguise?)
Pour définir une structure de texte d'élément, suivez les étapes ci-dessous :
Après
EItemType, créez une structure appeléeFItemTextavec une macroUSTRUCT(). Cette structure contient des données textuelles sur votre élément.C++// Contains textual data about the item. USTRUCT() struct FItemText { };Dans
FItemText, ajoutez la macroGENERATED_BODY().Ensuite, ajoutez deux propriétés
FTextnomméesNameetDescriptionpour stocker le nom et la description de cet élément. Ajouter la macroUPROPERTY()à chacune avec des argumentsEditAnywhere.C++// Contains textual data about the item. USTRUCT() struct FItemText { GENERATED_BODY() // The text name of the item. UPROPERTY(EditAnywhere) FText Name;
Créer la structure de données d'élément
Maintenant que vous avez ajouté ces déclarations de prérequis, créez la structure de données d'élément avec les propriétés de votre élément. Ces propriétés deviennent les champs de votre table de données.
Définir une structure nommée FItemData qui hérite de FTableRowBase. Ajouter le spécificateur public pour qu'il soit visible partout et ajouter GENERATED_BODY() pour l'outil d'en-tête de l'Unreal.
// Defines a basic item that can be used in a data table.
USTRUCT()
struct FItemData : public FTableRowBase
{
GENERATED_BODY()
};FTableRowBase est une structure de base fournie par l'Unreal Engine qui vous permet d'utiliser vos USTRUCTpersonnalisées dans une ressource de table de données. L'Unreal l'utilise pour savoir comment sérialiser votre structure de ligne, pour prendre en charge l'importation et l'exportation de données à partir de fichiers CSV/JSON, et pour assurer la sécurité du type lorsque vous extrayez des données d'un tableau.
Dans la structure FItemData, ajoutez les déclarations suivantes :
Un
FNameappeléidentifiant. Chaque ligne d'une table de données nécessite unFNameassocié à référencer.Une
enum EItemTypeappeléeItemType. C'est l'enum des types d'éléments que vous avez déclarée.Une structure
FItemTextappeléeItemText. C'est la structure de données de texte que vous avez déclarée.
Ajouter la macro UPROPERTY() à chaque déclaration avec les arguments EditAnywhere et Category = “Item Data”.
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
// The type of the item.
UPROPERTY(EditAnywhere, Category = "Item Data")
EItemType ItemType;
// Text struct including the item name and description.
UPROPERTY(EditAnywhere, Category = "Item Data")
Ajouter une déclaration de plus : un TObjectPtr à une UItemDefinition appelée ItemBase. Appliquez-lui la même macro UPROPERTY qu'aux autres propriétés de la structure.
// The Data Asset item definition associated with this item.
UPROPERTY(EditAnywhere, Category = "Item Data")
TObjectPtr<UItemDefinition> ItemBase;TObjectPtr est un type de pointeur intelligent de l'Unreal Engine qui permet de référencer les types dérivés de UObject de façon plus sûre. C'est un remplacement pour les pointeurs UObject bruts, compatible avec l'éditeur et le nettoyage de la mémoire. Il s'agit d'une référence figée, qui garde l'Objet chargé au temps d'exécution.
Dans l'étape suivante, vous allez créer une classe UDataAsset appelée ItemDefinition. Dans votre table de données, vous utiliserez le champ ItemBase pour référencer les instances de ressource de données ItemDefinition.
Votre structure FItemData devrait maintenant ressembler à ceci :
// Defines a basic item that can be used in a data table.
USTRUCT()
struct FItemData : public FTableRowBase
{
GENERATED_BODY()
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
Enregistrer votre code.
Créer une définition d'élément DataAsset
Jusqu'à présent, vous avez défini vos données d'élément ou le type de données qui apparaîtra dans votre table de données. Ensuite, vous allez implémenter la classe UItemDefinition pour laquelle vous avez fait une déclaration directe dans ItemData.h.
Cette classe hérite de UDataAsset et est donc un UObject. Vous pouvez donc créer et utiliser des instances de cette classe directement dans l'éditeur sans passer par le code. Vous allez remplir votre table de données avec des instances de votre classe UItemDefinition.
Pour créer votre classe ItemDefinition DataAsset (ItemDefinition.h), suivez ces étapes :
Dans l'Unreal Editor, allez sur Tools > New C++ Class.
Dans la fenêtre Choose Parent Class, cliquer sur All Classes.
Cherchez et sélectionnez DataAsset en tant que classe parente, puis cliquez sur Next.
Nommez la classe
ItemDefinition(c'est la déclaration directe que vous avez faite dansItemData.h), puis cliquez sur Create Class.
VS devrait automatiquement ouvrir les fichiers .h et .cpp de votre nouvelle classe. Sinon, actualisez VS et ouvrez les fichiers manuellement. Vous travaillerez uniquement dans le fichier .h, vous pouvez donc fermer le fichier .cpp si vous le souhaitez.
Dans ItemDefinition.h, ajouter un #include pour ItemData.h, car vous devrez réutiliser les propriétés ItemType et ItemText déclarées dans ce fichier.
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"Dans ItemDefinition.h, dans la macro UCLASS() au-dessus de la définition de classe, ajoutez les spécificateurs BlueprintType et Blueprintable pour l'exposer comme classe de base pour la création de blueprints.
// Defines a basic item with a static mesh that can be built from the editor.
UCLASS(BlueprintType, Blueprintable)
class FIRSTPERSON_API UItemDefinition : public UDataAsset
{
GENERATED_BODY()
public:
// Default constructor for the class.
UItemDefinition();
};Dans la section publique, copiez les déclarations FName ID, EItemType ItemType et FItemText ItemText de ItemData.h.
Votre définition d'élément obtient les mêmes données que la structure FItemData, ce qui vous évite d'avoir à référencer le tableau d'origine pour obtenir des informations sur l'élément.
public:
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
// The type of this item.
UPROPERTY(EditAnywhere, Category = "Item Data")
EItemType ItemType;
Après ItemText, déclarez un TSoftObjectPtr de type UStaticMesh appelé WorldMesh. On va utiliser ce maillage statique pour afficher cet objet dans le monde.
TSoftObjectPtr est un type spécial de pointeur faible qui fait office de représentation en chaîne de caractères pour une ressource qui n'est chargée que quand c'est nécessaire. Normalement, lors de la déclaration d'une propriété de pointeur UObject qui référence une ressource, cette ressource est chargée lorsque l'objet contenant la propriété est chargé. Ainsi, toutes les ressources seront chargées en même temps, ce qui entraînera une surcharge de mémoire et un ralentissement considérable. TSoftObjectPtr est utile pour les ressources volumineuses telles que les maillages, qui ne doivent se charger qu'à la demande. Pour plus d'informations, consultez la page Charger des ressources de façon asynchrone.
Ajouter la même macro UPROPERTY(EditAnywhere, Category = “Item Data”) à cette propriété.
// Text struct including the item name and description.
UPROPERTY(EditAnywhere, Category = "Item Data")
FItemText ItemText;
// The Static Mesh used to display this item in the world.
UPROPERTY(EditAnywhere, Category = "Item Data")
TSoftObjectPtr<UStaticMesh> WorldMesh;
Les lignes de la table de données sont similaires aux lignes du CSV. Elles servent à stocker des données textuelles plutôt que des ressources complètes. Pour optimiser la gestion des données, nous recommandons de regrouper les informations comme le maillage, le matériau et les animations d'un élément dans une ressource de données, car il s'agit de l'endroit central où sont conservées toutes les données sur un élément donné. La propriété de maillage statique de l'élément se trouve donc ici dans UItemDefinition plutôt que dans la structure FItemData.
Votre classe UItemDefinition devrait maintenant ressembler à ceci :
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
/**
* Defines a basic item with a static mesh that can be built from the editor.
Enregistrez votre code et compilez-le depuis Visual Studio.
Créer une instance de ressource de données
Avec vos données d'élément (struct FItemData de ItemData.h) et votre définition d'élément (classe UItemDefinition), vous avez tous les éléments nécessaires pour créer vos instances d'élément et votre table de données.
Créez d'abord une ressource de données pour un nouvel objet à ramasser au moyen d'un projectile, puis créez une table de données et remplissez-la avec les informations de votre ressource de données.
Pour créer un élément de ressource de données à partir de votre classe ItemDefinition, suivez les étapes ci-dessous :
Dans le navigateur de contenu, accédez au dossier Content > FirstPerson, cliquez sur Add ou faites un clic droit sur une zone vide de la vue Asset, sélectionnez New Folder et nommez-le
Data.C'est ici que vous allez stocker et organiser les ressources de données de votre jeu.
Dans le dossier
Data, cliquez sur ajouter ou faites un clic droit sur une zone vide de la vue Asset et sélectionnez Miscellaneous > Data Asset.Sélectionnez bien l'option Data Asset avec l'icône de graphique.
Dans la fenêtre Pick Class For Data Asset Instance, sélectionnez Item Definition (la classe C++ que vous avez définie précédemment), puis cliquez sur Select. Nommez la nouvelle ressource de données
DA_Pickup_001.Double-cliquez sur
DA_Pickup_001pour l'ouvrir. Dans le panneau Détails, vous verrez toutes les propriétés définies dansItemDefinition.h.Saisissez
pickup_001comme identifiant.Définissez le type d'élément sur Consumable.
Développez le texte d'élément Text et saisissez un nom et une description.
Réglez le maillage de monde sur
SM_FoamBullet.Cliquez sur Enregistrer près du coin supérieur gauche de la fenêtre pour enregistrer votre ressource de données.
Définir une table de données
Maintenant que vous avez au moins une ressource de données pour renseigner une table de données, vous pouvez créer le tableau !
Chaque ligne de la table de données est une instance remplie de votre structure ItemData.
Pour créer une table de données, procédez comme suit :
Dans le navigateur de contenu, dans votre dossier
Data, faites un clic droit sur une zone vide et sélectionnez Miscellaneous > Data Table.Dans la fenêtre Pick Row Structure, sélectionnez ItemData (la struct
FItemDataque vous avez définie dansItemData.h), puis cliquez sur OK.Nommez la nouvelle table
DT_PickupData, puis double-cliquez dessus pour l'ouvrir.
Votre table de données est vide au départ. Cependant, vous verrez les propriétés définies dans FItemData comme en-têtes en haut de la table ainsi qu'une colonne supplémentaire nommée Row Name.
Pour ajouter votre ressource de données d'objet à ramasser en tant que ligne dans la table, procédez comme suit :
Cliquez sur Add pour ajouter une nouvelle ligne à votre table. L'éditeur de table de données répertorie les entrées de ligne dans le panneau supérieur gauche de l'onglet Table de données.
Double-cliquez sur le nom de la ligne
NewRowet remplacez-le parpickup_001(l'identifiant de votre ressource de données).Vous pouvez utiliser n'importe quel
FNamecomme nom de ligne, mais pour faciliter la référence à la ligne dans le code, choisissez le même nom de ligne que l'identifiant de la ressource de données.Dans le panneau Row Editor, saisissez les mêmes valeurs que dans la ressource de données
DA_Pickup_001dans les champs ID, Item Type et Item Text.Réglez Item Base sur votre ressource de données
DA_Pickup_001.Enregistrez votre table de données.
Vous avez terminé ! Reprenez le diagramme des éléments de gameplay pilotés par les données que vous avez créés lors de cette étape et voyons comment ils sont connectés :
Vous avez créé une table de données qui obtient ses colonnes de la structure FItemData. Vous avez renseigné dans la table une ligne qui contient les données de l'instance de ressource de données ItemDefinition de type objet à usage unique que vous avez créée et vous avez utilisé le pointeur ItemBase pour faire référence à la ressource de données elle-même. Enfin, votre instance de ressource de données a obtenu ses propriétés de la classe de ressource de données parente UItemDefinition que vous avez créée.
Suivant
Dans la prochaine section, vous allez apprendre à enrichir cette définition d'élément pour créer une classe à ramasser personnalisée et l'instancier dans votre niveau.
Code terminé
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "ItemData.generated.h"
class UItemDefinition;
/**
* Defines the type of the item.
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
/**
* Defines a basic item with a static mesh that can be built from the editor.