PreyK / Unreal-Minimum-Viable-Flecs

Minimum viable Flecs entity component system implementation in Unreal Engine.
160 stars 14 forks source link

Is there any documentation on how to build off this template, add new systems, etc. for people new to Flecs? #4

Open joshuarobs opened 1 year ago

joshuarobs commented 1 year ago

I wish the readme would give an idea on how and where to add new systems, so you can build off this template, since I'm new to flecs and I don't know where to start to add my own functionality

PreyK commented 1 year ago

You can use the flecs documentation and manual for general flecs related questions.

The FlecsSubsystem in the project contains commented source code with 2 simple systems (the system that makes the corns grow and the system that copies them to unreal's ISM renderer) you can look at as a reference.

If you want something more in-depth you can check out the flecs space battle tutorial or the flecs C++ examples

Unfortunately, there's no tutorial series or docs on how to build off of this specific template, it was made more as a barebones minimal viable implementation for people who already have C++ and/or flecs/ecs experience just haven't used flecs with unreal before.

joshuarobs commented 1 year ago

@PreyK Is FlecsTest the folder we need to copy and paste so we can make our own modules from it? e.g. we can do FlecsMovement or FlecsDamage as custom modules. Or do we need to remove the Corn related functionality and put them into their own files? Because I'm trying the latter, but struggling to do so.

Its kind of confusing to me, because UFlecsSubsystem seems to be doing 1) general Flecs things and 2) corn related things. It looks like its doing 2 things together, and I'm trying to split them up so I can make this more modular and so I can understand how the code works.

It would be nice to have a simple list of instructions on how to add your own modules, e.g.:

  1. Duplicate or modify FlecsTest folder
  2. Create new blueprints, etc in UE5
  3. Code your functionality in C++
  4. Done

Thats just an example. I actually don't know what I'm supposed to do to extend functionality and to split them into different files. It seems like all I can do is just dump them all in the same file alongside the corn stuff, which doesn't seem optimal

PreyK commented 1 year ago

FlecsTest is the unreal project's name. The FlecsTest folder contains the "Game" C++ code, This is the main game module where all your game-related C++ code goes by Unreal convention.

The FlecsLibrary is an unreal library that gets linked to your "Game" library, in this case, FlecsTest, so you can use it in your Unreal project.

The FlecsSubsystem is an Unreal Engine world subsystem that in this case is responsible for everything flecs related. (corn logic is flecs related as it's implemented as a flecs system)

There is no go-to way to do this, you can split it out into different files based on what are your working conventions and your project requirements but it's mostly just boilerplate. If all you want is pure entities, components, and systems all you need is (based on the corn example):

Entity:

//make a new entity, add the FlecsCorn component
auto entity = GetEcsWorld()->entity().set<FlecsCorn>({0});

Component:

struct FlecsCorn
{
    float Growth;
};

System:

    //this system processes the growth of our entities
    auto system_grow = GetEcsWorld()->system<FlecsCorn>("Grow System")
    .iter([](flecs::iter it, FlecsCorn* fc) {
        float GrowthRate = 20*it.delta_time();
        for (int i : it) {
            //if we haven't grown fully (100) then grow
            fc[i].Growth+=(fc[i].Growth<100)*GrowthRate;
        }
    });

With the ECS logic, everything you want to implement you need to break out into Entity Component and System Component only stores data, it doesn't have logic. System iterates over all the relevant entities and processes the Component's data, System is responsible for your logic. Entity is well, an entity that can have Components

ECS is a purely functional programming concept, you don't "have" FlecsMovement or FlecsDamage modules, classes, etc.. You would have for example

Entities:

//make a new entity, add DamageComponent component
auto entity = GetEcsWorld()->entity().set<DamageComponent>({0});

Components:

struct DamageComponent
{
    float CurrentDamage;
};

And systems:

    //this system "slowly" heals the damage on our entities
    auto system_heal_damage= GetEcsWorld()->system<DamageComponent>("Damage Heal System")
    .iter([](flecs::iter it, DamageComponent* dc) {
        float HealRate = 20*it.delta_time();
        for (int i : it) {
            //if we haven't healed fully (damage>0) then reduce damage
            if(dc[i].CurrentDamage>0)
            {
                dc[i].CurrentDamage-=HealRate;
            }
        }
    });

And you can interact with them, for example add some damage to an entity:

auto GivenDamage = 50;
GetEcsWorld()->entity(YourEntityId).set<DamageComponent>({GivenDamage});

Edit: As for where it all goes it's completely up to you. You could make a world subsystem that's only responsible for handling the flecs world and adding entities and make FlecsDamage as a separate file where you define all your Components and Systems that are responsible for the "damage".

You could also put all your Components in one file (like a Components.h) and all your Systems in a separate file and that's a completely valid approach too.

If you only want to use flecs for one specific part of your game (like growing a lot of corn) you could make one world subsystem that handles everything related to that specific part (like in this case corn)

It's more like a coding convention problem than an optimization problem, all the above examples will run equally fast.