-
-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[v0.4] Add default initializer #173
Comments
import { addComponent, addEntity, createWorld, observe, onAdd } from 'bitecs'
const world = createWorld()
const eid = addEntity(world)
const Position = {
x: [] as number[],
y: [] as number[]
}
observe(world, onAdd(Position), (eid) => {
// Set default values when component is added
Position.x[eid] = 1
Position.y[eid] = 1
})
addComponent(world, eid, Position)
console.log(Position.x[eid]) // 1
console.log(Position.y[eid]) // 1
observe(world, onSet(Position), (eid,params) => {
Position.x[eid] = params.x
Position.y[eid] = params.y
})
addComponent(world, eid, set(Position, { x: 2 , y: 2 }))
console.log(Position.x[eid]) // 2
console.log(Position.y[eid]) // 2 hopefully this is sufficient for your needs? |
That's true, personally I like keeping things in the component itself and made this modification in my fork to test it out in a project, plus it's nice to avoid double initialization. It also decouples the default initializer from any particular world, eliminating the need for wrappers if there isn't a single or global world. Feel free to close in favor of onAdd! |
Actually, let's say I have a relation with a store. I can do this: export const CanTarget = createRelation(
withStore(() => ({
priority: [] as number[],
[$default]( eid, { target, priority = 0 } = {}) {
if ( target !== undefined ) {
CanTarget( target ).priority[ eid ] = priority
}
}
})),
)
const source = addEntity( world )
const target = addEntity( world )
addComponent( world, source,
Default( CanTarget( target ), { target, priority: 3 }),
) What would be your preferred solution for achieving this behavior with observables? Currently, the other eid is not passed to onAdd(). Should onAdd() just be modified to return both the source and target eid in a relation? |
perhaps this? export const CanTarget = createRelation(
// for 0.4 i can update withStore to supply the target, although not necessary for this particular case
withStore(() => {
const comp = { priority: [] as number[] }
observe(world, onAdd(comp), (eid) => { comp.priority[eid] = 0 })
observe(world, onSet(comp), (eid, params) => { comp.priority[eid] = params.priority })
return comp
})
)
const source = addEntity( world )
const target = addEntity( world )
addComponent(world, source, set(CanTarget( target ), { priority: 3 })) |
Thanks, I'll try that out! |
test( 'should properly set relation defaults', () => {
const world = createWorld()
const CanTarget = createRelation(
// for 0.4 i can update withStore to supply the target, although not necessary for this particular case
withStore(() => {
const comp = { priority: [] as number[] }
observe(world, onAdd(comp), (eid) => { comp.priority[eid] = 0 })
observe(world, onSet(comp), (eid, params) => { comp.priority[eid] = params.priority })
return comp
})
)
const source = addEntity( world )
const target = addEntity( world )
addComponent(world, source, set(CanTarget( target ), { priority: 3 }))
expect(CanTarget(target).priority[source]).toBe(3)
})
// AssertionError: expected +0 to be 3 // Object.is equality It seems onSet() runs before onAdd() in this case. |
Relatedly, what's the idiomatic way with bitECS to access stores of relations established a prefab? If I set CanTarget() on one prefab to another, is there a better way than iterating all of an entity's components and filtering for IsA and comparing each entityId? If one prefab sets CanTarget() for another prefab, and another inherits it and also sets CanTarget for the same prefab, but with a different priority, how is this reconciled in the store, when each prefab passes a different EID to onSet? It would just be nice to do something like const world = createWorld()
const Player = addPrefab( world )
const Enemy = addPrefab( world )
addComponent( world, Player, CanTarget( Enemy )) and to also have a way to automatically resolve inherited priority values. I can construct a blueprint or class which instantiates these relationships each time an entity is instantiated, but it feels more like data than behavior, that these relationships should be defined at the prefab level and can be performantly recovered later. Is this an abuse of relationships or prefabs? |
calling a relation on a target passes back the component for that pair in particular. the component is a store for any/all entities who have that relation to that entity. prefabs are entities themselves, except for the fact that they will not appear in queries. const world = createWorld()
const Contains = createRelation( withStore(() => ({ amount: [] as number[] }) ))
const Water = {}
const Bottle = addPrefab( world )
addComponent(world, Bottle, Contains(Water))
const Jug = addPrefab( world )
addComponent(world, Jug, Contains(Water))
const containsWaterComponent = Contains(Water)
containsWaterComponent.amount[Bottle] = 1
containsWaterComponent.amount[Jug] = 10
const bottleInstance = addEntity(world)
addComponent(world, bottleInstance, IsA(Bottle))
assert(containsWaterComponent.amount[bottleInstance] === containsWaterComponent.amount[Bottle]) keep in mind that because component stores are fully custom you will need to implement observe(world, onSet(Contains(Water)), (eid, params) => { Contains(Water).amount[eid] = params.amount })
observe(world, onGet(Contains(Water)), (eid) => Contains(Water).amount[eid] }) |
bitECS could benefit from a symbol-keyed default initializer. I've implemented one in #172, if it's something you want to include. If not, feel free to close this issue and corresponding PR.
It has the following semantics:
The text was updated successfully, but these errors were encountered: