NateTheGreatt/bitECS

What about boolean type in a component?

ProgrammingLife opened this issue ยท 6 comments

I've noticed that strings are not welcome to store in components. What about boolean properties? There is no such type in Types enum as I see. So how to use boolean in bitECS?

I think booleans are good for delayd events in components and many other things, when you can set something like this:

CharacterComponent.killedEvent[ eid ] = true;
// or:
CharacterComponent.isVisible[ eid ] = true;

I can use Types.i8 for a boolean type but I think it's not the best solution since it's too large type for just only two values (true/false, 0 or 1).

What do you think, guys?

fully on your side. right now I'm going with Types.i8 but as you mentioned it feels like a waste of resources

Im a bit puzzled by this also, for "events" kind of use there is a common pattern in ECS where you make tag components like by taking your case Visible or Dead with useful data inside like killerEid, killedTime etc. Then you could have your systems query for Not(Dead) entities and remove the component when not dead anymore. For example a GameModeSystem that respawns the entity after some time by setting the position and removing the Dead tag.

Instead of this, as a workaround, we can use integer but for using just only one int variable with bitwise operators, like:

export function hasFlag( a: number, b: number ): boolean {
	return ( a & b ) == b;
}

let state: number = 0;
// ... setting all boolean values to state int var as it would be boolean var
if ( hasFlag( state, booleansEnum.isVisibleFlag ) ) { ... } 
if ( hasFlag( state, booleansEnum.isDeadFlag ) ) { ... } 

All the flag vars can be stored in just one BooleansEnum { ... }.
In that way we can use only one variable in our bitECS component for all the boolean vars.
Though it makes a component less intuitive but more performant if our goal is a performance.

Instead of this, as a workaround, we can use integer but for using just only one int variable with bitwise operators, like:
Though it makes a component less intuitive but more performant if our goal is a performance.

I am not a pro in javascript performance, but I doubt this a bit... Using the least amount of bytes !== automatic better performance, and adding this kind of check within a loop might prevent compiler optimizations that rely on SIMD instructions, compared to using a tag component, in addition to iterating over objects unnecessarily

i.a.

const dead = deadCharactersQuery(world);
for(let i = 0; i<dead.length; i++) DeathTimer.time[dead[i]] += timeDelta;

const visible = visibleEnterQuery(world);
for(let i = 0; i<visible.length; i++) RenderObjects.opacity[visible[i]] = 1;

const invisible = visibleExitQuery(world);
for(let i = 0; i<invisible.length; i++) RenderObjects.opacity[invisible[i]] = 0.2;

Would only iterate over the subset of affected objects, and each loop can likely be done in a single CPU cycle with an SIMD instruction, whereas

const ents = allCharacterComponents(world);
for(let i = 0; i<ents.length; i++) {
  const eid = ents[i];

  if(hasFlag(CharacterComponents[eid].flags, BooleanEnums.dead)) {
    DeathTimer.time[dead[i]] += timeDelta;
  }

  if(hasFlag(CharacterComponents[eid].flags, BooleanEnums.visible)) {
    RendererObjects.opacity[eid] = 1;
  } else {
    RendererObjects.opacity[eid] = 0.2;
  }
}

will always loop through all objects, and cannot fully take advantage of bitECS's structs of arrays to do SIMD...

Although, I am quite a newbie at bitECS and not a pro in optimization, so I might be totally wrong...

@arthuro555
unfortunately SIMD is not accessible in JS land (yet?). the performance of bitECS in raw JS is from the cache coherency that results from keeping things in contiguous lists of single-type values (but indeed this is how SIMD prefers its data to be laid out).

@zewa666
as for boolean values, components themselves can act as booleans if you so desire.

e.g.

CharacterComponent.killedEvent[ eid ] = true;

can be this instead

addComponent(world, KilledComponent, eid)

otherwise storing a bool in ui8 is fine, imo. negligible amount of memory really