# Entities and Components ## Entities Entities represent things in a game. In a game there may be entities of characters, buildings, projectiles, particle effects etc. By itself, an entity is just an unique identifier without any data ### Creation ## Components A component is something that is added to an entity. Components can simply tag an entity ("this entity is an `Npc`"), attach data to an entity ("this entity is at `Position` `Vector3.new(10, 20, 30)`") and create relationships between entities ("bob `Likes` alice") that may also contain data ("bob `Eats` `10` apples"). ### Operations Operation | Description ----------|------------ `get` | Get a specific component or set of components from an entity. `add` | Adds component to an entity. If entity already has the component, `add` does nothing. `set` | Sets the value of a component for an entity. `set` behaves as a combination of `add` and `get` `remove` | Removes component from entity. If entity doesn't have the component, `remove` does nothing. `clear` | Remove all components from an entity. Clearing is more efficient than removing one by one. ### Components are entities In an ECS, components need to be uniquely identified. In Jecs this is done by making each component is its own unique entity. If a game has a component Position and Velocity, there will be two entities, one for each component. Component entities can be distinguished from "regular" entities as they have a `Component` component. An example: ::: code-group ```luau [luau] local Position = world:component() :: jecs.Entity print(world:has(Position, Jecs.Component)) ``` ```typescript [typescript] const Position = world.component(); print(world.has(Position, Jecs.Component)) ``` ::: All of the APIs that apply to regular entities also apply to component entities. This means it is possible to contexualize components with logic by adding traits to components ::: code-group ```luau [luau] local Networked = world:component() local Type = world:component() local Name = world:component() local Position = world:component() :: jecs.Entity world:add(Position, Networked) world:set(Posiition, Name, "Position") world:set(Position, Type, { size = 12, type = "Vector3" } ) -- 12 bytes to represent a Vector3 for id, ty, name in world:query(Type, Name):with(Networked) do local batch = {} for entity, data in world:query(id) do table.insert(batch, { entity = entity, data = data }) end -- entities are sized f64 local packet = buffer.create(#batch * (8 + ty.size)) local offset = 0 for _, entityData in batch do offset+=8 buffer.writef64(packet, offset, entityData.entity) if ty.type == "Vector3" then local vec3 = entity.data :: Vector3 offset += 4 buffer.writei32(packet, offset, vec3.X) offset += 4 buffer.writei32(packet, offset, vec3.Y) offset += 4 buffer.writei32(packet, offset, vec3.Z) end end updatePositions:FireServer(packet) end ``` ```typescript [typescript] const Networked = world.component() const Type = world.component() const Name = world.component() const Position = world.component(); world.add(Position, Networked) world.set(Posiition, Name, "Position") world.set(Position, Type, { size: 12, type: "Vector3" } ) // 12 bytes to represent a Vector3 for (const [id, ty, name] of world.query(Type, Name).with(Networked)) { const batch = new Array<{ entity: Entity, data: unknown}>() for (const [entity, data] of world.query(id)) { batch.push({ entity, data }) } // entities are sized f64 const packet = buffer.create(batch.size() * (8 + ty.size)) const offset = 0 for (const [_, entityData] of batch) { offset+=8 buffer.writef64(packet, offset, entityData.entity) if (ty.type == "Vector3") { const vec3 = entity.data as Vector3 offset += 4 buffer.writei32(packet, offsetm, vec3.X) offset += 4 buffer.writei32(packet, offset, vec3.Y) offset += 4 buffer.writei32(packet, offset, vec3.Z) } } updatePositions.FireServer(packet) } ``` :::