What's a room?

Started by
17 comments, last by Isvar Arthur 7 years, 7 months ago

No. What I'm suggesting is that the individual rooms ARE identifiers - they are arbitrary rectangles that identify sorting buckets in specific systems.


OK, got it.

Ironically, what you're describing is one of the most (over-)complicated forms of ECS. The original, simplest, and I would argue most useful form of ECS puts the components inside the systems, has systems directly communicate with one another by calling each other's methods, and makes "entities" be identifiers that allow systems to know which component instances on other systems represent the same object in the world.


Is there any documentation on this formulation of ECS? I realize it's basically a different way of doing the same thing, but there are always important subtleties that emerge sooner or later.

One question that arises is that if the components themselves are inside systems... Is it actually possible to create entities without knowledge of specific systems? In the "components live outside and are just data" formulation, the components are directly specified by whoever's creating the entity, without any specific knowledge of which systems will eventually process the components.

Advertisement

Is there any documentation on this formulation of ECS? I realize it's basically a different way of doing the same thing, but there are always important subtleties that emerge sooner or later.


That depends greatly on what you mean by "documentation." There are various GDC talks, blog posts and forum posts that explain the pattern. From a quick google search, it looks like they're being drowned out by articles based on the current Wiki article on ECS (or whatever that's based on), which advocates the approach where the components are stored separately from systems - and in all fairness, that IS more or less the only difference, but one that has big consequences on design.I'm not aware of any journal papers or the like. :)

This article has a brief overview of the different ECS variants. The one I'm advocating here is closest to Adam Martin's formulation as described therein (but weirdly not as Martin writes?).
Mike Acton's CppCon talk touches on some related concepts, but doesn't advocate "strict" ECS.

It's hard to find good sources on an arbitrary implementation of ECS because ECS is not a well-defined term. It's a group of related design patterns with subtle differences between them, some of which are more popular than others. I might be mistaken, so my formulation may not be the original, either, but it's the first one I remember seeing.

One question that arises is that if the components themselves are inside systems... Is it actually possible to create entities without knowledge of specific systems? In the "components live outside and are just data" formulation, the components are directly specified by whoever's creating the entity, without any specific knowledge of which systems will eventually process the components.


This isn't an important difference. Knowing about the components is functionally equivalent to knowing about the systems that own the components. This is true regardless - even if the components are stored outside the systems, they're still stored somewhere, and your creation logic needs to know about that place.

The important difference is that having the components be owned by the system allows other systems to not know about other systems' components directly. Remember, in a "pure ECS" of this type, all access to a component is gated through the system that owns the components of that type. All behaviour associated directly with the component is on the system that owns it. It gets a little funky when you have behaviour that affects two components that doesn't belong on a specific system, but this problem happens with "regular" OOP, too - and it's solved much the same way, by using free functions or creating another component type.

In my own code, the component structure definitions are usually private to the system that owns them. Other systems don't even know that those systems' component types exist, never mind being able to access them. On the other hand, I don't use strict ECS, and I actually think putting yourself in a box with strict ECS is counter-productive if you want to write clean code that is also shippable. If you really want strict ECS, it's probably best not to overarchitect it.

A room as a compound (not necessarily an entity as in an ECS) may have

* a NavigationComponent instance (navigation graph or mesh), to be installed with the NavigationServices;

* a set of ColliderComponent instances, perhaps tagged for specific usages, to be installed with the CollisionServices;

* a set of PortalComponent instances (of the entree or exit kind, interconnecting rooms inclusive positional mapping), to be installed with the NavigationServices, used as another level of navigation;

* perhaps a LocationComponent that gives the room a name, a location on a map, or such a thing

* ...

Not a RoomComponent makes the entity a room, but the collection of more generic components. We are not speaking of a strict and explicit typing here, but of a kind of duck typing.

That depends greatly on what you mean by "documentation."


Thanks for the sources!

In my own code, the component structure definitions are usually private to the system that owns them. Other systems don't even know that those systems' component types exist, never mind being able to access them.


I'm curious... Can you show me an example of entity construction under this system? In the T-machine variant of ECS (which is the one I'm looking at right now), construction ends up being something like:

auto e = Entities.create();
e.addComponent(Position(...));
e.addComponent(Renderable(...));

Typically with some kind of internal logic in addComponent that registers the entity with interested systems.

The idea of components remaining private to systems appeals to me far more than the basically duck-typed, expose-everything, share-everything T-machines approach.

If I were doing this (and I'm only a hobbyist, making small games), I'd create a full world (in physics-library terms) with all the rooms in it, and all the enemy's given their coordinates within the world, and the player has a coordinate within the world, starting in some "room." A room in my world would just be the current camera view, given some specific dimensions in my world (maybe I have it setup as 20 meters x 18 meters, which would map to a full screen in a window).

The camera component needs to know about the rooms dimensions and the player's location, so when a player walks through an opening and hits the portal at the edge of a room, the camera would shift to the next room.

You can work it so enemies are asleep unless the camera enters the room they are in, or, if you want Monsters alive always, you can have them doing their thing in the other rooms, I don't think it would hurt anything.

IMO, this is the easiest solution. There is no real "room" concept, except to the camera. Either way, whatever you decide, good luck!

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

I'm curious... Can you show me an example of entity construction under this system?


An NPC entity might get constructed like this:

// create the npc
if (auto npcDefinition = Definitions.NPCs.TryAndGet("elf"))
{
    auto e = Entities.create();
    Systems.Transforms.Create(e);
    Systems.Sprites.Create(e, npcDefinition->spriteDefinition);
    Systems.Characters.Create(e, npcDefinition->characterDefinition);
    Systems.Combat.Create(e, npcDefinition->characterDefinition);
    Systems.Monsters.Create(e, *npcDefinition);

    // create the weapon the NPC is holding
    if (auto weaponDefinition = Definitions.Weapons.TryAndGet(npcDefinition.weaponDefinitionName))
    {
        auto w = Entities.create();
        Systems.Transforms.Create(w);
        Systems.Sprites.Create(w, weaponDefinition->spriteDefinition);
        Systems.Weapons.Create(w, *weaponDefinition);
        Systems.Combat.RegisterHasWeapon(e, w);
    }
}
This isn't actually how I do this in my own code. I'll post a bit of my own code, but first a bit of context. I don't use a pure ECS in that I don't have one global system that represents entities. Some systems represent particular kinds of objects and other systems represent data that is temporarily or permanently affixed to them. So, for instance, sprite objects exist as simple sprites, while characters exist independently and maintain references to sprites that represent them. They aren't hard-tied to one sprite, either - a character could switch to use a different sprite object if wanted, though at this point I'm not taking advantage of that. Weapons and items work similarly. NPCs are a separate system that references characters and their associated sprites

Here is how I spawn NPCs:
// ---------------------------------------------------------------------------------------------
// GameplayLayer is the glue code that owns and connects gameplay-related systems
// "monster" == "NPC"
// "culling area" == "room" - culling areas live in their own system right now, but they don't have to
void GameplayLayer::RespawnMonsters()
{
    while (spawns.HasQueuedMonsters())
    {
        auto position = sf::Vector2f(spawns.NextQueuedMonster());
        auto cullingAreaIndex = culling.FindCurrentCullingAreaIndex(position);

        auto characterHandles = RespawnCharacter(position, "elf");
        monsters.Register(
            characters.GetCharacter(characterHandles.second).definition,
            sf::FloatRect(culling.cullingAreas[cullingAreaIndex]),
            characterHandles.first,
            characterHandles.second);
    }
}

// ---------------------------------------------------------------------------------------------
std::pair<TypedHandle, TypedHandle> GameplayLayer::RespawnCharacter(
    const sf::Vector2f position,
    const DatumString_t& characterName)
{
    auto characterDefinition = characters.FindDefinition(characterName);
    auto spriteDefinition = spriteObjects.FindDefinition(characterDefinition->spriteName);

    auto spriteHandle = spriteObjects.CreateSprite(
        spriteDefinition,
        position);

    auto characterHandle = characters.CreateCharacter(
        characterDefinition,
        spriteHandle,
        SpriteDirection::South,
        &spriteDefinition->idleAnimation.south);
    combat.RegisterCharacter(characterHandle, characterDefinition);

    if (characterDefinition->startingWeapon.length())
    {
        auto weaponDefinition = items.GetNamedWeaponDefinition(characterDefinition->startingWeapon);
        auto spriteDefinition = spriteObjects.FindDefinition(weaponDefinition->spriteName);
        auto spriteHandle = spriteObjects.CreateSprite(
            spriteDefinition,
            position);
        items.CreateBoundWeapon(characterHandle, weaponDefinition, spriteHandle);
        combat.RegisterWeapon(characterHandle, weaponDefinition);
    }

    return std::make_pair(spriteHandle, characterHandle);
}
This could use a bit of tidying, and again, it isn't quite ECS, but hopefully this should get the gist across.
An NPC entity might get constructed like this... [/quote] Thanks! Somewhat reassuring in that I saw exactly what I expected to see.

So - what exactly distinguishes a "space" from just a "scene"? If all of your systems, for example, only operate on things in a scene that you pass in as a parameter to the system's work functions? I have sort of run in to this issue lately - but it seems like using "scenes" works the same as spaces, where all entities contain an id indicating which scene they are in and they cannot actively be in more than one at a time - but they actually live in memory in some higher world object.


Space, scene, room, world, stage, etc. are all common names for the same thing*.

I prefer "space" because it means exactly what it says (it's a spatial collection), the concept was introduced to me that way, the engines I've used professionally that support the concept call them spaces, and Space is the Place.

* Mostly. Some engines come up with other creative applications of those nouns; Unity for instance calls it's levels "scenes" but doesn't let you load them into spatially distinct spaces. The engine I work in for my day job likewise calls it's levels "worlds" (and we also use them as level layers) but we had to hack in spaces support because it wasn't there originally.

Sean Middleditch – Game Systems Engineer – Join my team!

At first sight I remembered
about the classic 'Adventure'; from atari, because I think to be the starter of concept for rooms into an orgnized map and some of the elements you speak of. It got merit at its time.
Also first easter egg

This topic is closed to new replies.

Advertisement