Best way to refactor attached and detached, since they seem to be removed in 1.0 release
Closed this issue ยท 4 comments
Hi! I was trying to refactor my project to support the new 1.0 alpha release. Currently I'm a bit confused how to refactor something like this that uses the attached and detached handlers:
const actorsCreated = query(attached(Actor), Transform)
const actorsDestroyed = query(detached(Actor), Transform)
Could you provide some insight on this matter that what I should do to replace those? ๐
Also, thank you for the awesome library and the time and effort you have put into it! I really think this is the best JavaScript ECS implementation there is! ๐๐
Kind of answering my own question: I guess the new approach is to utilize the useMonitor
as such:
const actors = query(Actor, Transform)
useMonitor(
actors,
(entity, [component1, component2]) => { /* do something like initiate to world*/ },
(entity, [component1, component2]) => { /* do something like remove from world*/ }
)
This rises some questions as for example, I previously used a system to handle all this and now my actual actorSystem has nothing in it... ๐ Should these monitors be in their own files in a bigger project or...
Hey @panuchka, this is a great question.
You're exactly right: useMonitor
is planned to become the new way of handling attach/detach events. Although I've become fairly comfortable with the API, I'll admit its still a bit experimental and I'm totally open for suggestions.
I made the decision to remove attached and detached filters due to their implications on component storage and iteration. Each component needed a status that it transitioned between (orphaned, attached, detached), which complicated the data model a bit. Statuses also added small amount of overhead to iteration since filter-less queries needed to check if a component's status was "attached" to include the entity in its results.
useMonitor
is expected to be executed in the context of a system, e.g.
const physics = () => {
useMonitor(
actors,
(e, [a, t]) => simulation.createRigidBody(...),
(e) => simulation.destroyRigidBody(...),
)
}
If you feel that the useMonitor
callbacks have too much responsibility now, we could explore a different interface which returns iterable collections of entity/component tuples that transitioned into/out of the query, e.g.
const [enter, exit] = useMonitor(actors)
for (const [entity, [component1, component2]] of enter) {
...
}
The collections could then be consumed by systems which handle them, rather than requiring the side-effect logic be contained in the callbacks.
As always I am totally open to suggestions and actively looking for feedback. Thanks so much for the kind words! It makes the work I put into Javelin feel all the more rewarding.
Thank you for a quick reply! I managed to figure out that useMonitor
s are supposed to live in the systems by digging your examples in the next
branch ๐
I thought a while about the different approaches of useMonitor
and it is, in my opinion, much clearer than the previous attached/detached functionality. The two styles you presented with useMonitor
both have their ups and downs I think:
- Using
useMonitor
in its current form differs a bit from the regular flow of having to loop things yourself, but it is kind of nice that you don't have to do it yourself and can just pass a interation callback to it. - Using
useMonitor
when it returns just an iterable collections kind of feels even more clearer to me, I think. But then you have to loop it yourself ๐ I think I would prefer this one though. It kind of gives me the React hooks / Vue composition vibes.
All in all whichever you choose will be a good decision in the end ๐
@panuchka I just set up a community Discord for Javelin if you'd like to join: https://discord.gg/AbEWH3taWU.