Component fields order matters.
Closed this issue · 4 comments
Hi Andrew! @APB9785
Having a blast trying this out.
I'm trying to make a little factory simulator, and I noticed that the order of atoms in the fields
param for Component.add/3
seems to matter.
For example, I have a Producing
aspect.
defmodule Factory.Aspects.Producing do
@moduledoc """
Documentation for SampleAspect components.
"""
use ECSx.Aspect,
schema: {:entity_id, :type, :per_minute, :to}
end
Then when I add a component.
ECSx.Component.add(Producing, [id: id, to: to, type: :iron, per_minute: 30], [
:type,
:id,
:per_minute,
:to
])
The order of fields
changes which values are stored in which field. For example I'm logging all Producing
components:
ECSx.Component.get_all(Factory.Aspects.Producing, [:id, :type, :per_minute, :to])
|> IO.inspect()
Which prints the following. Notice my fields and values are incorrect.
[%{id: :iron, per_minute: 30, to: 4, type: 1}]
Perhaps I'm doing something wrong, or maybe this is a bug? Either way, I wanted to get your input on the fields
params. I wonder if it would be cleaner to exclude fields
from the function signature entirely?
i.e.
ECSx.Component.add(:aspect, [key: "value"])
Or am I misunderstanding, and there's a reason to include the fields
param?
Thank you again for making this and letting me try it out!
Ah yes, I thought this might come up. The Components
module isn't supposed to be used as the primary API for adding components. I'm still on the fence about having those functions be public at all, but I thought it could be helpful for some advanced case. The way you're supposed to add a Producing
component is to call Producing.add_component(entity_id: 123, other_field: :iron, per_minute: 30 ...)
, which in the end does delegate to Component.add
but it will automatically handle the fields in order, without having to specify them.
The presence of the fields is necessary because the output of that function is a map, and we need to know what keys to use for the map - the ordering is necessary because we are storing the components in ETS according to a certain ordering, and we need to know which element of the tuple corresponds to which key in the resulting map. It's probably possible to find a fully compile-time solution for this, right now the fields are being saved in a module attribute at compile time, which is then automatically accessed whenever one of the Aspect
functions is called.
Another note is that I recommend having the first field of each component be entity_id
so you can easily lookup whether an entity has that aspect or not.
Thanks for testing this out, @BrooklinJazz - I already see some important documentation that needs to be added.
Documentation updated