kennethloeffler / anatta

Anatta is a library that integrates the ECS pattern into Roblox.
https://kennethloeffler.github.io/anatta/
MIT License
38 stars 1 forks source link

Improve documentation website #78

Open kennethloeffler opened 3 years ago

kennethloeffler commented 3 years ago

The API reference is in an okay state. Some behavior needs to be documented more thoroughly:

Because ECS is uncommon on Roblox, there should also be good introductory material that gives a birds-eye view of the problems the library addresses and solutions that it provides:

grilme99 commented 3 years ago

Because ECS is uncommon on Roblox, there should also be good introductory material that gives a birds-eye view of the problems the library addresses and solutions that it provides:

Something I still struggle to wrap my head around is when/where you create entities (and tag them with components). For example, where do I tag a players character as a Character when it loads in? Am I supposed to make everything ECS? In this pattern, can there be scripts outside of ECS that handle tagging the player's character?

How does UI exist within this pattern? Can I use existing libraries I'm used to, such as Roact or even Fusion?

kennethloeffler commented 3 years ago

[...] when/where [do] you create entities (and tag them with components)?

It may take some getting used to the fact that a game object in Anatta is not represented by an Instance, but rather an entity with which many Instances can be associated in an explicit way. It can seem very backwards at first.

It's okay in Roblox to drive generative systems like this using the usual events: PlayerAdded, CharacterAdded, etc. For example, we might have a system on the server (using the components Player and Character) that looks something like the following:

-- get the world, import component definitions, etc...

local PlayerEntity = {
    fromInstance = {},
}

World:getReactor({
    withAll = { Player },
}):withAttachments(function(entity, player)
    return {
        player.CharacterAdded:Connect(function(character)
            registry:addComponent(entity, Character, character)
        end),

        player.CharacterRemoving:Connect(function()
            registry:removeComponent(entity, Character)
        end),
    }
end)

Players.PlayerAdded:Connect(function(player)
    local entity = registry:createEntity()

    PlayerEntity.fromInstance[player] = entity
    registry:addComponent(entity, Player, player)
end)

Players.PlayerRemoving:Connect(function(player)
    local entity = PlayerEntity.fromInstance[player]

    registry:destroyEntity(entity)
    PlayerEntity.fromInstance[player] = nil
end)

return PlayerEntity

One reason for creating the reverse mapping with PlayerEntity.fromInstance is that RemoteEvents pass the player as their first argument - and most of the time, we will want the entity representing the player, not the Player instance. It's possible to extend this system in all sorts of ways to meet various needs. Whether one considers it "inside" or "outside" ECS is up to them, but I'm inclined to say that it doesn't matter.

One quick thing to note - Anatta includes no built-in facilities for networking, as it is not something I believe belongs in the library. I'm not comfortable prescribing a one-size-fits-all solution for how people should network their games. Anatta provides the basic building blocks needed to do it well, and the additional code needed is fairly simple.

Am I supposed to make everything ECS? [...] Can I use existing libraries I'm used to, such as Roact or even Fusion?

The answer to the first question is definitely no! ECS sees the world as a flat collection of entities and components, and most games will need other kinds of data structures to do things like spatial queries. UI in Roblox is also a good example - although it is possible to use Anatta to make UI, there are already more developed paradigms that work well. There's no need to try to cram everything into an ECS world.

One way to think about Anatta is that it's the "glue" that holds various parts of the game logic together. It provides a notion of object identity which is more stable and more predictable than the one provided by the Roblox DOM (and serializable... but this is for another day), and a means to share game state in a well-defined way.