Extension d'UObject dans Unreal Engine 4

Bonjour à tous! Je m'appelle Alexander, je travaille avec Unreal Engine depuis plus de 5 ans, et presque tout ce temps - avec des projets de réseau.

Étant donné que les projets de réseau diffèrent dans leurs exigences de développement et de performances, il est souvent nécessaire de travailler avec des objets plus simples, tels que les classes UObject, mais leur fonctionnalité est initialement tronquée, ce qui peut créer un cadre solide. Dans cet article, je vais vous expliquer comment activer diverses fonctions dans la classe de base UObject dans Unreal Engine 4.



En fait, j'ai plutôt écrit l'article comme référence. La plupart des informations sont extrêmement difficiles à trouver dans la documentation ou la communauté, et ici vous pouvez rapidement ouvrir le lien et copier le code souhaité. J'ai décidé en même temps de partager avec vous! L'article s'adresse à ceux qui connaissent déjà un peu UE4. Le code C ++ sera considéré, bien qu'il ne soit pas nécessaire de le connaître. Vous pouvez simplement suivre les instructions si vous avez besoin de parler de quelque chose. De plus, il n'est pas nécessaire de tout copier, vous pouvez coller le code de la section avec les propriétés nécessaires et cela devrait fonctionner.



Un peu sur UObject


UObject est la classe de base de presque tout ce qui se trouve dans Unreal Engine 4. La grande majorité des objets créés dans votre monde ou simplement en mémoire en sont hérités: objets sur la scène (AActor), composants (UActorComponent), différents types de travail avec données et autres.

La classe elle-même, bien que plus simple que les dérivés, est en même temps assez fonctionnelle. Par exemple, il contient de nombreux événements utiles, tels que la modification des valeurs des variables dans l'éditeur et des fonctions de base du réseau, qui ne sont pas actives par défaut.

Les objets créés par cette classe ne peuvent pas être sur scène et existent exclusivement en mémoire. Ils ne peuvent pas être ajoutés en tant que composants aux acteurs, bien qu'il puisse s'agir d'une sorte de composant si vous implémentez vous-même les fonctionnalités nécessaires.

Pourquoi ai-je besoin d'UObject si AActor prend déjà en charge tout ce dont j'ai besoin? En général, il existe de nombreux exemples d'utilisation. Le plus simple est les articles en inventaire. Sur scène, quelque part dans le ciel, leur stockage est impossible, vous pouvez donc les stocker en mémoire sans charger le rendu et sans créer de propriétés inutiles. Pour ceux qui aiment les comparaisons techniques, AActor prend un kilo-octet (1016 octets) et un UObject vide ne fait que 56 octets.



Qu'est-ce qu'un problème UObject?


Il n'y a pas de problèmes en général, eh bien, ou je ne les ai tout simplement pas rencontrés. Tout ce qui agace UObject, c'est le manque de fonctionnalités diverses qui sont disponibles par défaut dans AActor ou dans les composants. Voici les problèmes que j'ai identifiés pour ma pratique:

  • Les UObjects ne sont pas répliqués sur le réseau;
  • en raison du premier point, nous ne pouvons pas déclencher d'événements RPC;
  • Vous ne pouvez pas utiliser un ensemble complet de fonctions qui nécessitent un lien vers le monde dans Blueprints;
  • ils n'ont pas d'événements standard comme BeginPlay et Tick;
  • vous ne pouvez pas ajouter de composants d'UObjects à AActor dans Blueprints.

La plupart des choses peuvent être facilement résolues. Mais certains devront bricoler.



Création d'UObject


Avant d'étendre notre classe avec des fonctionnalités, nous devons la créer. Utilisons l'éditeur pour que le générateur écrive automatiquement tout ce qui est nécessaire pour travailler dans l'en-tête (.h).

Nous pouvons créer une nouvelle classe dans l'éditeur de navigateur de contenu en cliquant sur le bouton Nouveau et en sélectionnant Nouvelle classe C ++ .



Ensuite, nous devons choisir la classe elle-même. Il peut ne pas être dans la liste générale, par conséquent, ouvrez-le et sélectionnez UObject.



Nommez votre classe et sélectionnez dans quel dossier elle sera stockée. Lorsque nous avons créé la classe, vous pouvez entrer dans le studio, la trouver et commencer à intégrer toutes les fonctions nécessaires.

Débutants, notez que deux fichiers sont créés: .h et .ccp. En .h, vous déclarerez des variables et des fonctions, et en .cpp vous définirez leur logique. Recherchez les deux fichiers dans votre projet. Si vous n'avez pas modifié le chemin, ils doivent se trouver dans Projet / Source / Projet /.

Jusqu'à ce que nous continuions, écrivons le paramètre Blueprintable dans la macro UCLASS () au-dessus de la déclaration de classe. Vous devriez obtenir quelque chose comme ceci:

.h

UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() } 

Grâce à cela, vous pouvez créer des Blueprints qui hériteront de tout ce que nous faisons avec cet objet.



Réplication UObject


Par défaut, les UObjects ne sont pas répliqués sur le réseau. Comme je l'ai décrit ci-dessus, un certain nombre de restrictions sont créées lorsque vous devez synchroniser des données ou une logique entre les parties, mais ne stockez pas de déchets dans le monde.

Dans Unreal Engine 4, la réplication a lieu précisément en raison des objets du monde. Cela signifie que la simple création d'un objet en mémoire et sa réplication échoueront. Dans tous les cas, vous aurez besoin d'un propriétaire qui gérera le transfert des données d'objet entre le serveur et les clients. Par exemple, si votre objet est la compétence d'un personnage, alors le personnage lui-même devrait devenir le propriétaire. Il sera également chef d'orchestre pour la transmission d'informations sur le réseau.

Préparez notre objet pour la réplication. Jusqu'à présent dans l'en-tête, nous devons définir une seule fonction:

.h

 UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() public: virtual bool IsSupportedForNetworking () const override { return true; }; } 

IsSupportedForNetworking () déterminera que l'objet prend en charge le réseau et peut être répliqué.

Cependant, tout n'est pas si simple. Comme je l'ai écrit ci-dessus, vous avez besoin d'un propriétaire qui contrôle le transfert de l'objet. Pour rendre l'expérience propre, créez un AActor qui le reproduira. Cela peut être fait exactement de la même manière que UObject, seule la classe parente, naturellement, AActor.

Débutants, si vous devez répliquer un objet dans un personnage, un contrôleur ou ailleurs, créez la classe de base appropriée via l'éditeur, ajoutez-y la logique nécessaire et héritez déjà de cette classe dans Blueprints.

À l'intérieur, nous avons besoin de 3 fonctions: un constructeur, une fonction pour répliquer des sous-objets, une fonction qui détermine ce qui est répliqué à l'intérieur de cet AActor (variables, références d'objet, etc.) et l'endroit où nous créons notre objet.

N'oubliez pas de créer une variable par laquelle notre objet sera stocké:

.h

 class MYPROPJECT_API AMyActor : public AActor { GENERATED_BODY() public: AMyActor(); virtual bool ReplicateSubobjects (class UActorChannel *Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override; void GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const override; virtual void BeginPlay (); UPROPERTY(Replicated, BlueprintReadOnly, Category="Object") class UMyObject* MyObject; } 

Dans le fichier source, nous devons tout écrire:

.cpp

 //  #include "MyActor.h" #include "Net/UnrealNetwork.h" #include "Engine/World.h" #include "Engine/ActorChannel.h" #include "   UObject/MyObject.h" AMyActor::AMyActor() { //  Actor  . bReplicates = true // . NetCullDistanceSquared = 99999; //  (  ). NetUpdateFrequency = 1.f; } void AMyActor::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) { Super::GetLifetimeReplicatedProps(OutLifetimeProps); //       .           . DOREPLIFETIME(AMyActor, MyObject); } bool AMyActor::ReplicateSubobjects(UActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) { bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags); //   . if (MyObject ) WroteSomething |= Channel->ReplicateSubobject(MyObject , *Bunch, *RepFlags); return WroteSomething; } AMyActor::BeginPlay() { /*       (  )  .    this.        . ,       ,     . */ if(HasAuthority()) { MyObject = NewObject<UMyObject>(this); //       if(MyObject) UE_LOG(LogTemp, Log, TEXT("%s created"), *MyObject->GetName()); } } 

Maintenant, votre objet sera répliqué avec cet acteur. Vous pouvez afficher son nom sur la coche, mais déjà sur le client. Veuillez noter que sur Begin Play, il est peu probable qu'un objet arrive avant le client, il est donc inutile d'écrire un journal dessus.



Réplication de variables dans UObject


Dans la plupart des cas, il est inutile de répliquer un objet s'il ne contient pas d'informations qui seront également synchronisées entre le serveur et les clients. Puisque notre objet est déjà répliqué, passer des variables n'est pas difficile. Cela se fait de la même manière qu'à l'intérieur de notre acteur:

.h

 UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() public: virtual bool IsSupportedForNetworking () const override { return true; }; void GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const override; UPROPERTY(Replicated, BlueprintReadWrite, Category="Object") int MyInteger; //   } 

.cpp

 //  #include "MyObject.h" #include "Net/UnrealNetwork.h" UMyObject ::UMyObject () { //  Object  .     . bReplicates = true //       ,     . } void UMyObject ::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) { Super::GetLifetimeReplicatedProps(OutLifetimeProps); //   Integer  . DOREPLIFETIME(UMyObject, MyInteger); } } 

En ajoutant une variable et en la marquant pour la réplication, nous pouvons la répliquer. Tout est simple et identique à celui d'AActor.

Cependant, il y a un petit piège qui n'est pas immédiatement visible, mais peut être trompeur. Cela sera particulièrement visible si vous créez votre UObject non pas pour travailler en C ++, mais le préparez pour l'héritage et travaillez dans Blueprints.

L'essentiel est que les variables créées dans l'héritier des Blueprints ne seront pas répliquées. Le moteur ne les marque pas automatiquement et la modification d'un paramètre sur le serveur dans le BP ne change rien dans la valeur sur le client. Mais il existe un remède à cela. Pour la réplication correcte des variables BP, vous devez les marquer à l'avance. Ajoutez quelques lignes à GetLifetimeReplicatedProps ():

.cpp

 void UMyObject ::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) { Super::GetLifetimeReplicatedProps(OutLifetimeProps); //   Integer  . DOREPLIFETIME(UMyObject, MyInteger); //       UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass()); if (BPClass) BPClass->GetLifetimeBlueprintReplicationList(OutLifetimeProps); } 

Les variables des classes Blueprint enfants se répliqueront désormais comme prévu.



Événements RPC dans UObject


Les événements RPC (Remote Procedure Call) sont des fonctions spéciales appelées de l'autre côté de l'interaction réseau d'un projet. En les utilisant, vous pouvez appeler la fonction depuis le serveur sur d'autres clients et depuis le client sur le serveur. Très utile et souvent utilisé lors de la rédaction de projets de réseau.

Si vous ne les connaissez pas, je vous recommande de lire un article. Il décrit l' utilisation en C ++ et en Blueprints .

Bien qu'il n'y ait aucun problème dans Actor ou dans les composants avec leur appel, dans les événements UObject, ils se déclenchent du même côté que celui où ils ont été appelés, ce qui rend impossible de faire un appel à distance lorsque cela est nécessaire.

En regardant le code du composant (UActorComponent), nous pouvons trouver plusieurs fonctions qui vous permettent de transférer des appels sur le réseau. Comme UActorComponent est hérité d'UObject, nous pouvons simplement copier les sections nécessaires de code et coller dans notre objet afin qu'il fonctionne comme il se doit:

.h

 //   #include "Engine/EngineTypes.h" UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() public: virtual bool CallRemoteFunction (UFunction * Function, void * Parms, struct FOutParmRec * OutParms, FFrame * Stack) override; virtual int32 GetFunctionCallspace (UFunction* Function, void* Parameters, FFrame* Stack) override; //   } 

.cpp

 //   #include "Engine/NetDriver.h" //       . bool UMyObject::CallRemoteFunction(UFunction * Function, void * Parms, FOutParmRec * OutParms, FFrame * Stack) { if (!GetOuter()) return false; UNetDriver* NetDriver = GetOuter()->GetNetDriver(); if (!NetDriver) return false; NetDriver->ProcessRemoteFunction(GetOuter(), Function, Parms, OutParms, Stack, this); return true; } int32 UMyObject::GetFunctionCallspace(UFunction * Function, void * Parameters, FFrame * Stack) { return (GetOuter() ? GetOuter()->GetFunctionCallspace(Function, Parameters, Stack) : FunctionCallspace::Local); } 

Avec ces fonctions, nous serons en mesure de déclencher des événements RPC non seulement dans le code, mais aussi dans les plans.

Veuillez noter que pour déclencher des événements Client ou Serveur, vous avez besoin d'un propriétaire dont le Propriétaire est notre joueur. Par exemple, l'objet appartient au personnage de l'utilisateur ou à l'objet dont le propriétaire est le contrôleur du joueur.



Fonctionnalités globales dans les plans directeurs


Si vous avez déjà créé un plan d'objet, vous avez peut-être remarqué que vous ne pouvez pas appeler des fonctions globales (statiques, mais pour plus de clarté, nous l'appelons) qui sont disponibles dans d'autres classes, par exemple, GetGamemode (). Il semble que vous ne pouvez tout simplement pas faire de classes dans les classes d'objets, à cause desquelles vous devez soit passer tous les liens lors de la création, soit pervertir, et parfois le choix tombe complètement sur la classe Actor qui est créée sur la scène et prend en charge tout.

Mais en C ++, bien sûr, il n'y a pas de tels problèmes. Cependant, le game designer, qui joue avec les paramètres et ajoute différentes petites choses, ne peut pas dire que vous devez ouvrir Visual Studio, trouver la classe appropriée et obtenir le mode de jeu dans la fonction doSomething () en changeant les points. Par conséquent, il est impératif que le concepteur puisse se connecter à Bluprint et en deux clics faire son travail. Économisez son temps et le vôtre. Cependant, les Blueprints ont été inventés pour cela.

L'essentiel est que lorsque vous recherchez ou appelez des fonctions dans le menu contextuel de Bluprint, ces mêmes fonctions globales qui nécessitent une référence au monde essaient d'appeler une fonction à l'intérieur de votre objet qui s'y réfère. Et si l'éditeur voit qu'il n'y a pas de fonction, il comprend qu'il ne peut pas l'utiliser et ne l'affiche pas dans la liste.



Cependant, il existe un remède à cela. Même deux.

Considérons d'abord une option pour une utilisation plus pratique dans l'éditeur. Nous devrons redéfinir une fonction qui renvoie un lien vers le monde, puis l'éditeur comprendra que dans le jeu lui-même, cela peut fonctionner:

.h

 UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() //  GetWorld()    . virtual UWorld* GetWorld() const override; //   } 

.cpp

 UWorld* UMyObject::GetWorld() const { //       ,    . if (GIsEditor && !GIsPlayInEditorWorld) return nullptr; else if (GetOuter()) return GetOuter()->GetWorld(); else return nullptr; } 

Maintenant, il est défini et l'éditeur comprendra qu'en général l'objet est capable d'obtenir le pointeur souhaité (bien qu'il ne soit pas valide) et d'utiliser des fonctions globales dans le BP.

Veuillez noter que le propriétaire (GetOuter ()) doit également avoir accès au monde. Il peut s'agir d'un autre UObject avec un objet GetWorld (), un composant ou un acteur spécifique dans la scène.



Il y a une autre façon. Il suffit d'ajouter une étiquette à la macro UCLASS () lors de la déclaration de la classe selon laquelle le paramètre WorldContextObject sera ajouté aux fonctions statiques du BP, dans lequel tout objet servant de conducteur au "monde" et aux fonctions globales du moteur est alimenté. Cette option convient à ceux qui dans le projet peuvent avoir plusieurs mondes en même temps (par exemple, le monde du jeu et le monde pour le spectateur):

.h

 //   WorldContext      UCLASS(Blueprintable, meta=(ShowWorldContextPin)) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() //   } 

Si vous entrez GetGamemode dans la recherche dans le BP, il apparaîtra dans la liste, comme d'autres fonctions similaires, et le paramètre sera WorldContextObject, dans lequel vous devez passer un lien vers Actor.



Soit dit en passant, vous pouvez simplement y déposer le propriétaire de notre propriété. Je recommande de créer une fonction sur Actor, elle sera toujours utile pour l'objet:

.h

 UCLASS(Blueprintable, meta=(ShowWorldContextPin)) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() //      ,     . public: UFUNCTION(BlueprintPure) AActor* GetOwner() const {return Cast<AActor>(GetOuter());}; //   } 

Maintenant, vous pouvez simplement utiliser les fonctions globales en combinaison avec notre fonction Pure pour obtenir le propriétaire.



Si vous déclarez également GetWorld () dans la deuxième variante comme dans la première variante, vous pouvez vous soumettre une référence (Self ou This) dans le paramètre WorldContextObject.



Événements BeginPlay et Tick


Un autre problème que les développeurs de Blueprint peuvent rencontrer est qu'il n'y a aucun événement BeginPlay et Tick dans la classe Object. Bien sûr, vous pouvez les créer vous-même et appeler depuis une autre classe. Mais vous devez admettre que c'est beaucoup plus pratique lorsque tout fonctionne hors de la boîte.

Commençons par comprendre comment lancer Begin Play. Nous pouvons créer une fonction disponible pour la réécriture dans le BP et l'appeler dans le constructeur de la classe, mais il y a un certain nombre de problèmes, car au moment du constructeur votre objet n'a pas encore été complètement initialisé.

Dans toutes les classes, il existe la fonction PostInitProperties (), qui est appelée après l'initialisation de la plupart des paramètres et l'enregistrement de l'objet dans divers systèmes internes, par exemple pour le garbage collector. Dans ce document, vous pouvez simplement appeler notre événement, qui sera utilisé dans les Blueprints:

.h

 UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() //      . virtual void PostInitProperties() override; // ,      . UFUNCTION(BlueprintImplementableEvent) void BeginPlay(); //   } 

.cpp

 void UMyObject::PostInitProperties() { Super::PostInitProperties(); //   ,   .   BeginPlay    if(GetOuter() && GetOuter()->GetWorld()) BeginPlay(); } 

Au lieu de if (GetOuter () && GetOuter () -> GetWorld ()), vous pouvez simplement mettre if (GetWorld ()) si vous l'avez déjà redéfini.

Faites attention! Par défaut, PostInitProperties () est également appelé dans l'éditeur.

Maintenant, nous pouvons aller dans notre objet BP et appeler l'événement BeginPlay. Il sera appelé lors de la création de l'objet.

Passons à l'événement Tick. Il n'y a pas de fonction simple pour nous. Cochez les objets dans le moteur appelle un gestionnaire spécial, auquel vous devez en quelque sorte ramasser. Cependant, il existe une astuce très pratique ici - l'héritage supplémentaire de FTickableGameObject. Cela vous permettra de faire automatiquement tout ce dont vous avez besoin, puis il vous suffira de choisir les fonctions nécessaires:

.h

 //   #include "Tickable.h" //   c FTickableGameObject UCLASS(Blueprintable) class MYPROPJECT_API UMyObject : public UObject, public FTickableGameObject { GENERATED_BODY() public: //   virtual void Tick(float DeltaTime) override; virtual bool IsTickable() const override; virtual TStatId GetStatId() const override; protected: //     UFUNCTION(BlueprintImplementableEvent) void EventTick(float DeltaTime); //   } 

.cpp

 void UMyObject::Tick(float DeltaTime) { //       . EventTick(DeltaTime); //     . } //     bool UMyObject::IsTickable() const { return true; } TStatId UMyObject::GetStatId() const { return TStatId(); } 

Si vous héritez de votre objet et créez une classe BP, un événement EventTick sera disponible, ce qui provoquera une logique pour chaque trame.



Ajout de composants à partir d'UObjects


Dans les plans directeurs UObject, vous ne pouvez pas générer de composants pour les acteurs. Le même problème est inhérent aux plans directeurs d'ActorComponent. La logique d'Epic Games n'est pas très claire, car en C ++ cela peut être fait. De plus, vous pouvez ajouter un composant Actor à un autre objet Actor en spécifiant simplement un lien. Mais cela ne peut pas être fait.

Malheureusement, je n'ai pas pu comprendre cet article. Si quelqu'un a des instructions sur la façon de procéder, je serai heureux de le publier ici.

La seule option que je peux offrir pour le moment est de créer un wrapper dans la classe UObject, donnant accès à une simple addition de composants. Ainsi, il sera possible d'ajouter des composants à l'Actor, mais vous n'aurez pas de paramètres d'entrée créés dynamiquement du spawn. Souvent, cela peut être négligé.



Configuration d'une instance via l'éditeur


Dans UE4, il existe une autre «fonctionnalité» pratique pour travailler avec des objets: il s'agit de la possibilité de créer une instance lors de l'initialisation et de modifier ses paramètres via l'éditeur, définissant ainsi ses propriétés, sans créer de classe enfant uniquement à des fins de paramétrage. Particulièrement utile pour les concepteurs de jeux.

Supposons que vous ayez un gestionnaire de modificateurs pour un personnage et que les modificateurs eux-mêmes soient représentés par des classes qui décrivent les effets superposés. Le game designer a créé une paire de modificateurs et indique dans le gestionnaire ceux qui sont utilisés.

Dans une situation normale, cela ressemblerait à ceci:

.h

 class MYPROPJECT_API AMyActor : public AActor { GENERATED_BODY() public: UPROPERTY(EditAnywhere) TSubclassOf<class UMyObject> MyObjectClass; } 



Cependant, il y a un problème en ce qu'il ne peut pas configurer de modificateurs et vous devez créer une classe supplémentaire pour d'autres valeurs. D'accord, ce n'est pas très pratique d'avoir des dizaines de classes dans le navigateur de contenu qui ne diffèrent que par des valeurs. Il est facile de résoudre ce problème. Vous pouvez ajouter quelques champs dans USTRUCT (), et également indiquer dans l'objet conteneur que nos objets seront des instances, et pas seulement des références à des objets ou des classes inexistants:

.h

 UCLASS(Blueprintable, DefaultToInstanced, EditInlineNew) //  -        class MYPROPJECT_API UMyObject : public UObject { GENERATED_BODY() UPROPERTY(EditAnywhere) //       uint8 MyValue; // ,    //   } 

Cela seul ne suffit pas, il faut maintenant indiquer que la même variable avec la classe sera une instance. Cela est déjà fait où vous stockez l'objet, par exemple, dans le gestionnaire de modificateurs de caractères:

.h

 class MYPROPJECT_API AMyActor : public AActor { GENERATED_BODY() public: UPROPERTY(EditAnywhere, Instanced) //   Instanced    class UMyObject* MyObject; //    } 

Veuillez noter que nous utilisons la référence à l'objet, et non à la classe, car l'instance sera créée immédiatement lors de l'initialisation. Maintenant, nous pouvons aller dans la fenêtre de l'éditeur pour sélectionner une classe et ajuster les valeurs à l'intérieur de l'instance. C'est beaucoup plus pratique et plus flexible.

image



Info


Il existe une autre classe intéressante dans Unreal Engine. C'est AInfo. Une classe héritée d'AActor qui n'a pas de représentation visuelle dans le monde. Info utilise des classes telles que: mode de jeu, GameState, PlayerState et autres. Autrement dit, les classes qui prennent en charge différentes puces d'AActor, par exemple, la réplication, mais ne sont pas placées sur la scène.

Si vous devez créer un gestionnaire global supplémentaire qui devrait prendre en charge le réseau et toutes les classes d'acteurs résultantes, vous pouvez l'utiliser. Vous n'avez pas besoin de manipuler la classe UObject comme décrit ci-dessus pour la forcer, par exemple, à répliquer des données.

Cependant, gardez à l'esprit que même si l'objet n'a pas de coordonnées, pas de composants visuels et qu'il ne s'affiche pas à l'écran, il est toujours un descendant de la classe Actor, ce qui signifie qu'il est aussi lourd que le parent. Raisonnablement utilisé en petites quantités et pour plus de commodité.



Conclusion


UObject est très souvent nécessaire, et je vous conseille de l'utiliser chaque fois que l'acteur n'est pas vraiment nécessaire. C'est dommage que ce soit un peu limité, mais c'est aussi un plus. Parfois, vous devez bricoler lorsque vous devez utiliser un modèle personnalisé, mais surtout, toutes les principales restrictions peuvent être supprimées.

, , UObject, , , .

, , Unreal Engine 4. - , . , - , UObject.

Source: https://habr.com/ru/post/fr475622/


All Articles