Question about Architecture of game and classes

Hi. I’m studying UE and I have a question about the architecture of the game. We have 50 NPCs per 10 square meters. A lot. They have a Gene component. The gene stores the level of the gene, the generation number, and a mutation array. A mutation is essentially a change of npc’s skin (a couple of mash/material, plus the name of the mutation). We have more then 100 Mutations, a lot in general. Each new gene level gives a random mutation from a list of all mutations. How best to implement mutation storage and management in UE and how to refer to this structure/class/object from C++?

  • A new Actor for every mutation is unnecessary. And Actors for rendering objects, right? We don’t need to spawn anything, we need just to keep information and manage it.
  • Making a separate MutationManager Actor, which would store an array of all mutations as structures. But again, I don’t need to spawn it… It should somehow be embedded into the engine process and just store an array of structures and implement methods to work with that list. And the gen component will just call the right methods when it needs to.
  • Player controller? But it has nothing to do with the player.
  • The npc actor? Why would every npc need to store a huge array of all mutations, even if it’s just pointers to mutations or pointers to mash/material pairs? AI controller is the same. It’s create instance for each npc, lots of duplications.
  • GameMode? I don’t think so… Game Mode for different things.

How to make it as optimized and logical as possible in terms of architecture, both the engine and the principles of OOP in general?

Sorry in case of mistakes in English. I’m not Native.

I don’t think that all that I wrote is the best of the best, but maybe it can give you a hint )

instead, new actor you can construct new object.

if your “mutation controller” shouldn’t store states, then you can implement a mutation static library for mutation functions only.

if you need to store mutation state for each player you can store it in your GeneComponent and also add relative logic in this ActorComponent, but if you need to store after player killed then choose a separate object in the player controller (you can think about it as a part which controls player mutation).

A list of the mutations could be stored in data asset.

in your GeneComponent or Player or NPC or whatever, you need to create binding or interface on LevelUp event. But I guess, that main question is who response for calculation current mutation level.

If mutation level constant in game - call mutation library to get right parameters on initialization stage of the Component/MutationComponent.
But if your NPC’s, mutation players growing up in the game process, you should separate which process responsible for that.

Simple example, inflicted damage - accumulate directly in the Pawn like health. if damage over 100 then call LevelUp dispatcher or MutationComponent event in case of simple mutation logic. in case of multiplayer, place this controller in Game Mode and call through controller.

in case of complex pawn level up logic, better to use separate controller who can calculate resources for level up and make relative events as LevelUp for this pawn. create separate object too for storing current experience/damage, calculating it and calling LevelUp (in case if it is independent of Mutation at all). this object you can store in the same manner as mutation/gene component - pawn, controller (better controller, pawn for “cosmetic” reason, he can die or change his “shell”). But if this logic not depends only on the NPC or player actions, then you should provide manager and put in the game mode (more server specific) or game instance (persistence between levels, but exists separately between server and clients).

1 Like

First of all thank you for great reply! =)

Just some extra details about logic of gene in game:

Gene Component have array with CurrentMutations. Level of gene = CurrentMutation.Num(). Examples: “Long arms” mutation, “Big Head” mutation atc. Each mutation just contain one or more parts of Modular Character Skeleton Mesh (big head mesh for example) and material for this mesh. No states =) Gene combine all Mutations and applying changes to Character Mesh. This is what Gene for.

Gene randomly add new Mutation to CurrentMutations from AllMutationList after LevelUp.

Very simple logic =)

Main Question how to make this AllMutationsList and where to store and manage. Because we can have more then 100 of them. Then i can just make pointer in GeneComponent to this AllMutationsList or MutationManager and operate with it from there.
And i want to have possibility manage this List from UE Editor in Details. In this case Level Designer can simply create or edit All Mutation List from there.

If i understood right i have to do some research regarings UObject for Manager and Data Assets for AllMutationList. Am I right? =)

LevelUps is not a problem btw =) I have very specific logic for it, it’s not about experience =) I already have place where i’m calling for it.

for storing data - you can choose for designer data tables (without embedded structs) or assets .

I guess, you can read about similar loading DataAsset approach here.

and if I understand correctly that you don’t need to manage this data somehow in game logic, but in game editor, then you don’t need any kind of object manager.

as I can see (:
GeneComponent:

  1. receive LevelUp,
  2. ask static library (if there are many functions, then MutationLibrary) to pickup random mutation from DataAsset,
  3. apply mutation on his owner Pawn.

Hi! After some research i do the following:

MutationsDeveloperSettings from DeveloperSettings.
with

TArray<TSoftObjectPtr<UMutationDataAsset>> Mutations;

Then i have created DataAsset for each Mutation with mesh and material that i need. After that i have added it to project settings in my new Developer Settings page with all mutations.
And last one… in GeneComponent:

const UMutationsDeveloperSettings* MutationsSettings = GetDefault<UMutationsDeveloperSettings>();
if (MutationsSettings) {
	auto Mutations = MutationsSettings->Mutations;
	if (Mutations.Num() > 0) {
		srand(__rdtsc());
		CurrentMutations.Add(Mutations[rand() % Mutations.Num()].LoadSynchronous());
	}
}

Of course i also need logic to apply mutation, but it’s not a problem at all =)

What do you think of this approach?

cool)

also, you can check for the further architectural reasons UE Programming Subsystems