This series is a response to the request of many people and is thought on the
one hand to explain the design and ideas behind many choices in EnTT
, on the
other to show how this library can be used to develop something from
scratch.
In no case is the aim to teach programming a game (since I’m not the right
person to do it) but I’ll try to show how the various EnTT
modules can be
combined to achieve something that works in this sense. I’ll probably focus on
something simple because I’m not that skilled overall and the goal is to explore
the various features of the library, not to make the game of the year one step
at a time. Afterwards, nothing excludes that the series takes a turn towards
more complex applications, just for the sake of continuing to write it. :)
A library of libraries
Let’s start with the basics: what EnTT
is
and how I recommend using it.
Unfortunately, I’ve read or been told several times that EnTT
is very big.
I’d like to debunk a myth: it is not. EnTT
is a library of libraries, mainly
completely unrelated to each other and usable independently.
So, why didn’t you create many different projects instead? Good question.
My goal has never been to have one or many high-rated projects on GitHub. My aim
was and is to have something that I can clone and that offers me all together
those tools that I’ve developed and continue to develop over the years.
The alternative was to make EnTT
a project full of submodules. However, I’m
not a supporter of submodules. Honestly, it had to be comfortable for me first
of all and I find this form much much easier to use and to work with.
This doesn’t prevent users from cloning the repository and using only a small
part of it.
I’ve seen many projects that rely only on the ECS part or only on its runtime
reflection system, even someone who clones it and then uses only the delegate
and little else.
This is… just… fine? Using a class from EnTT
won’t tie a project to all
the other parts of the library.
So there are no dependencies between modules?
It depends on the definition of dependencies actually.
Literally all files refer to the core
directory for definitions such as
ENTT_ASSERT
or ENTT_NOEXCEPT
. That is to say, a single configuration file
(or kind of) to rule them all.
If this type of dependencies is a problem and not acceptable, then yes, this
library is not for you. If this is fine instead, the other dependencies are
likely irrelevant to you anyway. They are most of the times things that are
convenient but configurable and can be by-passed.
For example, the default pool in the registry offers signal for when a component
is created or destroyed. To do that, it uses the signal class from the signal
directory.
Is this an inevitable dependency? Definitely no. The default pool type is fully
customizable. Signal support can be removed or replaced with your own class,
thus removing the dependency on the built-in signal class.
Then there are modules that are completely unrelated to the rest of the library.
An example of this are many of the classes in the signal
folder.
On the other hand, some types make use of features offered by other modules and
therefore depend on them. For example, the registry
class makes use of the
internal RTTI system (core/type_info
) and would not work without it. In these
cases, although the dependencies are minimal, these are things that would have
been replicated had I chosen to create separate repositories.
So, at the end of the day, the answer is that yes, there are some dependencies. A very few ones though, mainly due to things closely related to the module in use and such that you would have wanted to have anyway.
If there are few dependencies, why do I import everything every time anyway?
Most likely because you’ve decided to use the single include file or to include
entt.hpp
from inside src
. Well, don’t and life will smile at you.
These files are intended for small tests or for online use. For example, the
single include file was a convenience for testing and reproducing errors on
godbolt. And I say was because thankfully godbolt now
includes EnTT as a library and it’s possible to
use the files inside src
directly, making it much easier to work with.
The only reason I personally see for using the single include file or entt.hpp
directly is to increase parsing and compile times, without any real
benefit.
EnTT
is designed as a library of libraries and I constantly try to insert in
each file the minimum necessary for the functionality that belongs to it.
Even including only registry.hpp in my headers doesn’t help either
Likely, in fact you shouldn’t most of the time.
A long time ago now, a very smart guy suggested filling EnTT
with what I would
call forward files and I’ve been keeping them up-to-date ever since.
For example, if your header only refers to a generic reference to a registry
(such as const entt::registry &
), why include registry.hpp
at all, risking
that every change will propagate to who knows how many cpp files?
Try including entt/entity/fwd.hpp
instead next time. Do it for all your
headers, do it wherever you need it, do it for every EnTT
class you find
yourself using.
EnTT is a header-only library and I personally have no problem using a super
optimized, tested and well known std::vector
rather than reinventing the wheel
by offering something worse and with a dubious interface, just because the
gamedev world often wants it.
Of course, this doesn’t mean that we should be crazy or deny one of the most
critical aspects of C++, that is, compilation time. However, we can do a lot to
mitigate the problem as long as we know the right means.
Believe me, I don’t like to heat my room with my laptop and that’s why I’m in a
constant process to reduce inclusions, instantiations and so on. Though, the
first step is to start using the right tools that already exist!
Oh, and anyway EnTT is too modern C++, it doesn’t work on consoles
Really? I thought Bedrock ran on consoles. :)
Why do we keep talking about this point all the time? It works, that’s it, C++17
is widely accepted nowadays, you don’t have to worry about it anymore.
Okay, you convinced me, where do I start?
From the next posts (which I’ll try to keep short for the whole series, trying
to publish frequently).
Next time I’ll try to give you an idea of what’s really in this library. Module
by module, just a quick look. Then we’ll delve into one module at a time, one
class at a time, one design choice at a time and one example at a time, with
some explanations on lesser known C++ techniques used here and there.
This is just the beginning of this journey. I’ve waited a long time, many have asked me about this, finally it’s time to start it.
Let me know that it helped
I hope you enjoyed what you’ve read so far.
If you liked this post and want to say thanks, consider to star the GitHub project that hosts this blog. It’s the only way you have to let me know that you appreciate my work.
Thanks.