Compare commits

...

6 commits

Author SHA1 Message Date
Ukendio
52e03683db Update documentation
Some checks are pending
analysis / Run Luau Analyze (push) Waiting to run
deploy-docs / build (push) Waiting to run
deploy-docs / Deploy (push) Blocked by required conditions
publish-npm / publish (push) Waiting to run
unit-testing / Run Luau Tests (push) Waiting to run
2025-01-15 19:59:07 +01:00
Ukendio
753adf884d Bump 2025-01-15 19:24:14 +01:00
Ukendio
bc11bd9cff Merge unit testing 2025-01-15 13:11:22 +01:00
Ukendio
d85a8914d9 Fix names on workflow files 2025-01-15 13:10:05 +01:00
Marcus
fb372d7e09
Update unit-testing.yaml 2025-01-15 13:08:32 +01:00
Laud Boateng
bce46bc93f
Fix World Each to iterate multiple archetypes (#174)
* Add tests for entity-child relationships and removal in world queries, fixed world_each

* Correct test case

---------

Co-authored-by: Marcus <ukendio@gmail.com>
2025-01-15 13:03:28 +01:00
11 changed files with 820 additions and 680 deletions

View file

@ -1,19 +1,19 @@
name: Analysis
on: [push, pull_request, workflow_dispatch]
jobs:
run:
name: Run Luau Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Luau
uses: encodedvenom/install-luau@v2.1
- name: Analyze
run: |
output=$(luau-analyze src || true) # Suppress errors for now.
name: analysis
on: [push, pull_request, workflow_dispatch]
jobs:
run:
name: Run Luau Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Luau
uses: encodedvenom/install-luau@v2.1
- name: Analyze
run: |
output=$(luau-analyze src || true) # Suppress errors for now.

View file

@ -1,64 +1,64 @@
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: Deploy VitePress site to Pages
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # or pnpm install / yarn install / bun install
- name: Build with VitePress
run: npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: deploy-docs
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # or pnpm install / yarn install / bun install
- name: Build with VitePress
run: npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View file

@ -1,17 +1,17 @@
name: Publish to NPM
name: publish-npm
on:
push:
branches: main
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: "20"
- uses: JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_AUTH_TOKEN }}
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: "20"
- uses: JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_AUTH_TOKEN }}

View file

@ -1,71 +1,71 @@
name: Release
name: release
on:
push:
tags: ["v*"]
push:
tags: ["v*"]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Rokit
uses: CompeyDev/setup-rokit@v0.1.2
- name: Install Rokit
uses: CompeyDev/setup-rokit@v0.1.2
- name: Install Dependencies
run: wally install
- name: Install Dependencies
run: wally install
- name: Build
run: rojo build --output build.rbxm default.project.json
- name: Build
run: rojo build --output build.rbxm default.project.json
- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: build
path: build.rbxm
- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: build
path: build.rbxm
release:
name: Release
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Project
uses: actions/checkout@v4
release:
name: Release
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Download Jecs Build
uses: actions/download-artifact@v3
with:
name: build
path: build
- name: Download Jecs Build
uses: actions/download-artifact@v3
with:
name: build
path: build
- name: Rename Build
run: mv build/build.rbxm jecs.rbxm
- name: Rename Build
run: mv build/build.rbxm jecs.rbxm
- name: Create Release
uses: softprops/action-gh-release@v1
with:
name: Jecs ${{ github.ref_name }}
files: |
jecs.rbxm
- name: Create Release
uses: softprops/action-gh-release@v1
with:
name: Jecs ${{ github.ref_name }}
files: |
jecs.rbxm
publish:
name: Publish
needs: [release]
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
publish:
name: Publish
needs: [release]
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Rokit
uses: CompeyDev/setup-rokit@v0.1.2
- name: Install Rokit
uses: CompeyDev/setup-rokit@v0.1.2
- name: Wally Login
run: wally login --token ${{ secrets.WALLY_AUTH_TOKEN }}
- name: Wally Login
run: wally login --token ${{ secrets.WALLY_AUTH_TOKEN }}
- name: Publish
run: wally publish
- name: Publish
run: wally publish

View file

@ -1,31 +1,31 @@
name: Unit Testing
on: [push, pull_request, workflow_dispatch]
jobs:
run:
name: Run Luau Tests
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Luau
uses: encodedvenom/install-luau@v4.3
with:
version: 'latest'
verbose: 'true'
- name: Run Unit Tests
id: run_tests
run: |
output=$(luau test/tests.luau)
echo "$output"
if [[ "$output" == *"0 fails"* ]]; then
echo "Unit Tests Passed"
else
echo "Error: One or More Unit Tests Failed."
exit 1
fi
name: unit-testing
on: [push, pull_request, workflow_dispatch]
jobs:
run:
name: Run Luau Tests
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Install Luau
uses: encodedvenom/install-luau@v4.3
with:
version: "latest"
verbose: "true"
- name: Run Unit Tests
id: run_tests
run: |
output=$(luau test/tests.luau)
echo "$output"
if [[ "$output" == *"0 fails"* ]]; then
echo "Unit Tests Passed"
else
echo "Error: One or More Unit Tests Failed."
exit 1
fi

View file

@ -1,88 +1,110 @@
# Query
A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components.
# Methods
## with
Adds components (IDs) to query with, but will not use their data. This is useful for Tags or generally just data you do not care for.
```luau
function query:with(
...: Entity -- The IDs to query with
): Query
```
Example:
::: code-group
```luau [luau]
for id, position in world:query(Position):with(Velocity) do
-- Do something
end
```
```ts [typescript]
for (const [id, position] of world.query(Position).with(Velocity)) {
// Do something
}
```
:::
:::info
Put the IDs inside of `world:query()` instead if you need the data.
:::
## without
Removes entities with the provided components from the query.
```luau
function query:without(
...: Entity -- The IDs to filter against.
): Query -- Returns the Query
```
Example:
::: code-group
```luau [luau]
for entity, position in world:query(Position):without(Velocity) do
-- Do something
end
```
```ts [typescript]
for (const [entity, position] of world.query(Position).without(Velocity)) {
// Do something
}
```
:::
## archetypes
Returns the matching archetypes of the query.
```luau
function query:archetypes(): { Archetype }
```
Example:
```luau [luau]
for i, archetype in world:query(Position, Velocity):archetypes() do
local columns = archetype.columns
local field = archetype.records
local P = field[Position]
local V = field[Velocity]
for row, entity in archetype.entities do
local position = columns[P][row]
local velocity = columns[V][row]
-- Do something
end
end
```
:::info
This function is meant for people who want to really customize their query behaviour at the archetype-level
:::
# Query
A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components.
# Methods
## iter
Returns an iterator that can be used to iterate over the query.
```luau
function Query:iter(): () -> (Entity, ...)
```
## with
Adds components (IDs) to query with, but will not use their data. This is useful for Tags or generally just data you do not care for.
```luau
function Query:with(
...: Entity -- The IDs to query with
): Query
```
Example:
::: code-group
```luau [luau]
for id, position in world:query(Position):with(Velocity) do
-- Do something
end
```
```ts [typescript]
for (const [id, position] of world.query(Position).with(Velocity)) {
// Do something
}
```
:::
:::info
Put the IDs inside of `world:query()` instead if you need the data.
:::
## without
Removes entities with the provided components from the query.
```luau
function Query:without(
...: Entity -- The IDs to filter against.
): Query -- Returns the Query
```
Example:
::: code-group
```luau [luau]
for entity, position in world:query(Position):without(Velocity) do
-- Do something
end
```
```ts [typescript]
for (const [entity, position] of world.query(Position).without(Velocity)) {
// Do something
}
```
:::
## archetypes
Returns the matching archetypes of the query.
```luau
function Query:archetypes(): { Archetype }
```
Example:
```luau [luau]
for i, archetype in world:query(Position, Velocity):archetypes() do
local columns = archetype.columns
local field = archetype.records
local P = field[Position]
local V = field[Velocity]
for row, entity in archetype.entities do
local position = columns[P][row]
local velocity = columns[V][row]
-- Do something
end
end
```
:::info
This function is meant for people who want to really customize their query behaviour at the archetype-level
:::
## cached
Returns a cached version of the query. This is useful if you want to iterate over the same query multiple times.
```luau
function Query:cached(): Query -- Returns the cached Query
```

View file

@ -1,402 +1,500 @@
# World
A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components and to perform different kinds of operations on them.
# Functions
## new
`World` utilizes a class, meaning JECS allows you to create multiple worlds.
```luau
function World.new(): World
```
Example:
::: code-group
```luau [luau]
local world = jecs.World.new()
local myOtherWorld = jecs.World.new()
```
```ts [typescript]
import { World } from "@rbxts/jecs";
const world = new World();
const myOtherWorld = new World();
```
:::
# Methods
## entity
Creates a new entity.
```luau
function World:entity(): Entity
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
```
```ts [typescript]
const entity = world.entity();
```
:::
## component
Creates a new component. Do note components are entities as well, meaning JECS allows you to add other components onto them.
These are meant to be added onto other entities through `add` and `set`
```luau
function World:component<T>(): Entity<T> -- The new componen.
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number> -- Typecasting this will allow us to know what kind of data the component holds!
```
```ts [typescript]
const Health = world.component<number>();
```
:::
## get
Returns the data present in the component that was set in the entity. Will return nil if the component was a tag or is not present.
```luau
function World:get<T>(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to fetch
): T?
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
print(world:get(Entity, Health))
-- Outputs:
-- 100
```
```ts [typescript]
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
print(world.get(Entity, Health))
// Outputs:
// 100
```
:::
## has
Returns whether an entity has a component (ID). Useful for checking if an entity has a tag or if you don't care of the data that is inside the component.
```luau
function World:has(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to check
): boolean
```
Example:
::: code-group
```luau [luau]
local IsMoving = world:component()
local Ragdolled = world:entity() -- This is a tag, meaning it won't contain data
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
world:add(Entity, Ragdolled)
print(world:has(Entity, Health))
print(world:has(Entity, IsMoving)
print(world:get(Entity, Ragdolled))
print(world:has(Entity, Ragdolled))
-- Outputs:
-- true
-- false
-- nil
-- true
```
```ts [typescript]
const IsMoving = world.component();
const Ragdolled = world.entity(); // This is a tag, meaning it won't contain data
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
world.add(Entity, Ragdolled);
print(world.has(Entity, Health));
print(world.has(Entity, IsMoving));
print(world.get(Entity, Ragdolled));
print(world.has(Entity, Ragdolled));
// Outputs:
// true
// false
// nil
// true
```
:::
## add
Adds a component (ID) to the entity. Useful for adding a tag to an entity, as this adds the component to the entity without any additional values inside
```luau
function World:add(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to add
): void
```
::: info
This function is idempotent, meaning if the entity already has the id, this operation will have no side effects.
:::
## set
Adds or changes data in the entity's component.
```luau
function World:set(
entity: Entity, -- The entity
id: Entity<T>, -- The component ID to set
data: T -- The data of the component's type
): void
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
print(world:get(Entity, Health))
world:set(Entity, Health, 50)
print(world:get(Entity, Health))
-- Outputs:
-- 100
-- 50
```
```ts [typescript]
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
print(world.get(Entity, Health))
world.set(Entity, Health, 50);
print(world.get(Entity, Health))
// Outputs:
// 100
// 50
```
:::
## query
Creates a [`query`](query) with the given components (IDs). Entities that satisfies the conditions of the query will be returned and their corresponding data.
```luau
function World:query(
...: Entity -- The components to query with
): Query
```
Example:
::: code-group
```luau [luau]
-- Entity could also be a component if a component also meets the requirements, since they are also entities which you can add more components onto
for entity, position, velocity in world:query(Position, Velocity) do
end
```
```ts [typescript]
// Roblox-TS allows to deconstruct tuples on the act like if they were arrays!
// Entity could also be a component if a component also meets the requirements, since they are also entities which you can add more components onto
for (const [entity, position, velocity] of world.query(Position, Velocity) {
// Do something
}
```
:::
:::info
Queries are uncached by default, this is generally very cheap unless you have high fragmentation from e.g. relationships.
:::
## target
Get the target of a relationship.
This will return a target (second element of a pair) of the entity for the specified relationship. The index allows for iterating through the targets, if a single entity has multiple targets for the same relationship.
If the index is larger than the total number of instances the entity has for the relationship or if there is no pair with the specified relationship on the entity, the operation will return nil.
```luau
function World:target(
entity: Entity, -- The entity
relation: Entity, -- The relationship between the entity and the target
nth: number, -- The index
): Entity? -- The target for the relationship at the specified index.
```
## parent
Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.
```luau
function World:parent(
child: Entity -- The child ID to find the parent of
): Entity? -- Returns the parent of the child
```
This operation is the same as calling:
```luau
world:target(entity, jecs.ChildOf, 0)
```
## contains
Checks if an entity or component (id) exists in the world.
```luau
function World:contains(
entity: Entity,
): boolean
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
print(world:contains(entity))
print(world:contains(1))
print(world:contains(2))
-- Outputs:
-- true
-- true
-- false
```
```ts [typescript]
const entity = world.entity();
print(world.contains(entity));
print(world.contains(1));
print(world.contains(2));
// Outputs:
// true
// true
// false
```
:::
## remove
Removes a component (ID) from an entity
```luau
function World:remove(
entity: Entity,
component: Entity<T>
): void
```
Example:
::: code-group
```luau [luau]
local IsMoving = world:component()
local entity = world:entity()
world:add(entity, IsMoving)
print(world:has(entity, IsMoving))
world:remove(entity, IsMoving)
print(world:has(entity, IsMoving))
-- Outputs:
-- true
-- false
```
```ts [typescript]
const IsMoving = world.component();
const entity = world.entity();
world.add(entity, IsMoving);
print(world.has(entity, IsMoving));
world.remove(entity, IsMoving);
print(world.has(entity, IsMoving));
// Outputs:
// true
// false
```
:::
## delete
Deletes an entity and all of its related components and relationships.
```luau
function World:delete(
entity: Entity
): void
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
print(world:has(entity))
world:delete(entity)
print(world:has(entity))
-- Outputs:
-- true
-- false
```
```ts [typescript]
const entity = world.entity();
print(world.has(entity));
world.delete(entity);
print(world.has(entity));
// Outputs:
// true
// false
```
:::
## clear
Clears all of the components and relationships of the entity without deleting it.
```luau
function World:clear(
entity: Entity
): void
```
# World
A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components and to perform different kinds of operations on them.
# Functions
## new
`World` utilizes a class, meaning JECS allows you to create multiple worlds.
```luau
function World.new(): World
```
Example:
::: code-group
```luau [luau]
local world = jecs.World.new()
local myOtherWorld = jecs.World.new()
```
```ts [typescript]
import { World } from "@rbxts/jecs";
const world = new World();
const myOtherWorld = new World();
```
:::
# Methods
## entity
Creates a new entity.
```luau
function World:entity(): Entity
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
```
```ts [typescript]
const entity = world.entity();
```
:::
## component
Creates a new component. Do note components are entities as well, meaning JECS allows you to add other components onto them.
These are meant to be added onto other entities through `add` and `set`
```luau
function World:component<T>(): Entity<T> -- The new componen.
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number> -- Typecasting this will allow us to know what kind of data the component holds!
```
```ts [typescript]
const Health = world.component<number>();
```
:::
## get
Returns the data present in the component that was set in the entity. Will return nil if the component was a tag or is not present.
```luau
function World:get<T>(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to fetch
): T?
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
print(world:get(Entity, Health))
-- Outputs:
-- 100
```
```ts [typescript]
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
print(world.get(Entity, Health));
// Outputs:
// 100
```
:::
## has
Returns whether an entity has a component (ID). Useful for checking if an entity has a tag or if you don't care of the data that is inside the component.
```luau
function World:has(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to check
): boolean
```
Example:
::: code-group
```luau [luau]
local IsMoving = world:component()
local Ragdolled = world:entity() -- This is a tag, meaning it won't contain data
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
world:add(Entity, Ragdolled)
print(world:has(Entity, Health))
print(world:has(Entity, IsMoving)
print(world:get(Entity, Ragdolled))
print(world:has(Entity, Ragdolled))
-- Outputs:
-- true
-- false
-- nil
-- true
```
```ts [typescript]
const IsMoving = world.component();
const Ragdolled = world.entity(); // This is a tag, meaning it won't contain data
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
world.add(Entity, Ragdolled);
print(world.has(Entity, Health));
print(world.has(Entity, IsMoving));
print(world.get(Entity, Ragdolled));
print(world.has(Entity, Ragdolled));
// Outputs:
// true
// false
// nil
// true
```
:::
## add
Adds a component (ID) to the entity. Useful for adding a tag to an entity, as this adds the component to the entity without any additional values inside
```luau
function World:add(
entity: Entity, -- The entity
id: Entity<T> -- The component ID to add
): void
```
::: info
This function is idempotent, meaning if the entity already has the id, this operation will have no side effects.
:::
## set
Adds or changes data in the entity's component.
```luau
function World:set(
entity: Entity, -- The entity
id: Entity<T>, -- The component ID to set
data: T -- The data of the component's type
): void
```
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number>
local Entity = world:entity()
world:set(Entity, Health, 100)
print(world:get(Entity, Health))
world:set(Entity, Health, 50)
print(world:get(Entity, Health))
-- Outputs:
-- 100
-- 50
```
```ts [typescript]
const Health = world.component<number>();
const Entity = world.entity();
world.set(Entity, Health, 100);
print(world.get(Entity, Health));
world.set(Entity, Health, 50);
print(world.get(Entity, Health));
// Outputs:
// 100
// 50
```
:::
## query
Creates a [`query`](query) with the given components (IDs). Entities that satisfies the conditions of the query will be returned and their corresponding data.
```luau
function World:query(
...: Entity -- The components to query with
): Query
```
Example:
::: code-group
```luau [luau]
-- Entity could also be a component if a component also meets the requirements, since they are also entities which you can add more components onto
for entity, position, velocity in world:query(Position, Velocity) do
end
```
```ts [typescript]
// Roblox-TS allows to deconstruct tuples on the act like if they were arrays!
// Entity could also be a component if a component also meets the requirements, since they are also entities which you can add more components onto
for (const [entity, position, velocity] of world.query(Position, Velocity) {
// Do something
}
```
:::
:::info
Queries are uncached by default, this is generally very cheap unless you have high fragmentation from e.g. relationships.
:::
## target
Get the target of a relationship.
This will return a target (second element of a pair) of the entity for the specified relationship. The index allows for iterating through the targets, if a single entity has multiple targets for the same relationship.
If the index is larger than the total number of instances the entity has for the relationship or if there is no pair with the specified relationship on the entity, the operation will return nil.
```luau
function World:target(
entity: Entity, -- The entity
relation: Entity, -- The relationship between the entity and the target
nth: number, -- The index
): Entity? -- The target for the relationship at the specified index.
```
## parent
Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil.
```luau
function World:parent(
child: Entity -- The child ID to find the parent of
): Entity? -- Returns the parent of the child
```
This operation is the same as calling:
```luau
world:target(entity, jecs.ChildOf, 0)
```
## contains
Checks if an entity or component (id) exists in the world.
```luau
function World:contains(
entity: Entity,
): boolean
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
print(world:contains(entity))
print(world:contains(1))
print(world:contains(2))
-- Outputs:
-- true
-- true
-- false
```
```ts [typescript]
const entity = world.entity();
print(world.contains(entity));
print(world.contains(1));
print(world.contains(2));
// Outputs:
// true
// true
// false
```
:::
## remove
Removes a component (ID) from an entity
```luau
function World:remove(
entity: Entity,
component: Entity<T>
): void
```
Example:
::: code-group
```luau [luau]
local IsMoving = world:component()
local entity = world:entity()
world:add(entity, IsMoving)
print(world:has(entity, IsMoving))
world:remove(entity, IsMoving)
print(world:has(entity, IsMoving))
-- Outputs:
-- true
-- false
```
```ts [typescript]
const IsMoving = world.component();
const entity = world.entity();
world.add(entity, IsMoving);
print(world.has(entity, IsMoving));
world.remove(entity, IsMoving);
print(world.has(entity, IsMoving));
// Outputs:
// true
// false
```
:::
## delete
Deletes an entity and all of its related components and relationships.
```luau
function World:delete(
entity: Entity
): void
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
print(world:has(entity))
world:delete(entity)
print(world:has(entity))
-- Outputs:
-- true
-- false
```
```ts [typescript]
const entity = world.entity();
print(world.has(entity));
world.delete(entity);
print(world.has(entity));
// Outputs:
// true
// false
```
:::
## clear
Clears all of the components and relationships of the entity without deleting it.
```luau
function World:clear(
entity: Entity
): void
```
## each
Iterate over all entities with the specified component.
Useful when you only need the entity for a specific ID and you want to avoid creating a query.
```luau
function World:each(
id: Entity -- The component ID
): () -> Entity
```
Example:
::: code-group
```luau [luau]
local id = world:entity()
for entity in world:each(id) do
-- Do something
end
```
```ts [typescript]
const id = world.entity();
for (const entity of world.each(id)) {
// Do something
}
```
:::
## children
Iterate entities in root of parent
```luau
function World:children(
parent: Entity -- The parent entity
): () -> Entity
```
This is the same as calling:
```luau
world:each(pair(ChildOf, parent))
```

View file

@ -1995,6 +1995,7 @@ local function world_each(world: World, id): () -> ()
archetype = archetypes[archetype_id]
entities = archetype.entities
row = #entities
entity = entities[row]
end
row -= 1
return entity

View file

@ -1,6 +1,6 @@
{
"name": "@rbxts/jecs",
"version": "0.5.1",
"version": "0.5.2",
"description": "Stupidly fast Entity Component System",
"main": "jecs.luau",
"repository": {

View file

@ -900,20 +900,39 @@ end)
TEST("world:children", function()
local world = world_new()
local e1 = world:entity()
local e2 = world:entity()
local e3 = world:entity()
local C = world:component()
local T = world:entity()
local e1 = world:entity()
world:set(e1, C, true)
local e2 = world:entity()
world:add(e2, T)
world:add(e2, pair(ChildOf, e1))
local e3 = world:entity()
world:add(e3, pair(ChildOf, e1))
for entity in world:children(pair(ChildOf, e1)) do
local count = 0
for entity in world:children(e1) do
count += 1
if entity == e2 or entity == e3 then
CHECK(true)
continue
end
CHECK(false)
end
CHECK(count == 2)
world:remove(e2, pair(ChildOf, e1))
count = 0
for entity in world:children(e1) do
count += 1
end
CHECK(count == 1)
end)
TEST("world:clear()", function()
@ -1562,9 +1581,9 @@ TEST("repro", function()
local world = world_new()
local component1 = world:component()
local tag1 = world:entity()
local query = world:query(component1):with(tag1):cached()
local entity = world:entity()
world:set(entity, component1, "some data")

View file

@ -1,6 +1,6 @@
[package]
name = "ukendio/jecs"
version = "0.5.1"
version = "0.5.2"
registry = "https://github.com/UpliftGames/wally-index"
realm = "shared"
license = "MIT"