This commit is contained in:
Raghav Arora 2025-02-21 00:11:38 +01:00 committed by GitHub
commit 86de60f97f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1499 additions and 339 deletions

View file

@ -1,33 +1,45 @@
<p align="center">
<img src="assets/image-5.png" width=35%/>
</p>
<div align="center">
<img src="assets/image-5.png" width="240" alt="Jecs Logo"/>
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge)](LICENSE) [![Wally](https://img.shields.io/github/v/tag/ukendio/jecs?&style=for-the-badge)](https://wally.run/package/ukendio/jecs)
# Jecs
### Just a Stupidly Fast Entity Component System
Just a stupidly fast Entity Component System
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge)](LICENSE)
[![Wally](https://img.shields.io/github/v/tag/ukendio/jecs?&style=for-the-badge)](https://wally.run/package/ukendio/jecs)
</div>
- [Entity Relationships](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) as first class citizens
- Iterate 800,000 entities at 60 frames per second
- Type-safe [Luau](https://luau-lang.org/) API
- Zero-dependency package
- Optimized for column-major operations
- Cache friendly [archetype/SoA](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) storage
- Rigorously [unit tested](https://github.com/Ukendio/jecs/actions/workflows/ci.yaml) for stability
## ✨ Features
### Example
- 🚀 **Blazing Fast:** Iterate over 800,000 entities at 60 FPS
- 🔗 **Entity Relationships:** First-class support for [entity relationships](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c)
- 📝 **Type Safety:** Fully typed [Luau](https://luau-lang.org/) API
- 🎯 **Zero Dependencies:** Simple integration with no external dependencies
- ⚡ **Optimized Storage:** Cache-friendly [archetype/SoA](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) storage
- ✅ **Battle-tested:** [Rigorously tested](https://github.com/Ukendio/jecs/actions/workflows/ci.yaml) for stability
## 🚀 Example Usage
```lua
local world = jecs.World.new()
local pair = jecs.pair
local Position = world:component() :: jecs.Id<Vector3>
local Velocity = world:component() :: jecs.Id<Vector3>
-- These components and functions are actually already builtin
-- but have been illustrated for demonstration purposes
local ChildOf = world:component()
local Name = world:component()
local entity = world:entity()
world:set(entity, Position, Vector3.new(0, 0, 0))
world:set(entity, Velocity, Vector3.new(1, 0, 0))
local function parent(entity)
return world:target(entity, ChildOf)
-- Update system (example)
for id, position, velocity in world:query(Position, Velocity) do
world:set(id, Position, position + velocity)
end
local function getName(entity)
return world:get(entity, Name)
end
@ -44,7 +56,6 @@ world:add(sara, pair(ChildOf, alice))
world:set(sara, Name, "sara")
print(getName(parent(sara)))
for e in world:query(pair(ChildOf, alice)) do
print(getName(e), "is the child of alice")
end
@ -55,10 +66,35 @@ end
-- sara is the child of alice
```
21,000 entities 125 archetypes 4 random components queried.
![Queries](assets/image-3.png)
Can be found under /benches/visual/query.luau
## ⚡ Performance
Inserting 8 components to an entity and updating them over 50 times.
### Query Performance
21,000 entities, 125 archetypes, 4 random components queried:
![Queries](assets/image-3.png)
*Benchmark source: /benches/visual/query.luau*
### Insertion Performance
Inserting 8 components to an entity and updating them over 50 times:
![Insertions](assets/image-4.png)
Can be found under /benches/visual/insertions.luau
*Benchmark source: /benches/visual/insertions.luau*
## 📖 Documentation
- [Getting Started](docs/learn/overview/get-started.md)
- [API Reference](docs/api/jecs.md)
- [Examples](examples/)
- [Common Issues](docs/learn/faq/common-issues.md)
## 🤝 Contributing
Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting pull requests.
## 💬 Community
- Join our [Discord](https://discord.gg/h2NV8PqhAD)
- Report issues on [GitHub](https://github.com/ukendio/jecs/issues)
## 📄 License
Jecs is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
```

8
docs/.vitepress/404.md Normal file
View file

@ -0,0 +1,8 @@
# Page Not Found
Sorry, the page you're looking for doesn't exist.
- [Go to Home](/)
- [View Documentation](/learn/overview/get-started)
- [API Reference](/api/jecs)
- [Report an Issue](https://github.com/ukendio/jecs/issues)

View file

@ -4,7 +4,7 @@ import { defineConfig } from 'vitepress'
export default defineConfig({
title: "Jecs",
base: "/jecs/",
description: "A VitePress Site",
description: "Just a stupidly fast Entity Component System",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
@ -16,11 +16,11 @@ export default defineConfig({
sidebar: {
"/api/": [
{
text: "API reference",
text: "API Reference",
items: [
{ text: "jecs", link: "/api/jecs" },
{ text: "World", link: "/api/world" },
{ text: "Query", link: "/api/query" }
{ text: 'Jecs', link: '/api/jecs' },
{ text: 'World', link: '/api/world' },
{ text: 'Query', link: '/api/query' }
]
}
],
@ -38,32 +38,41 @@ export default defineConfig({
{ text: 'Entities and Components', link: '/learn/concepts/entities-and-components' },
{ text: 'Queries', link: '/learn/concepts/queries' },
{ text: 'Relationships', link: '/learn/concepts/relationships' },
{ text: 'Component Traits', link: 'learn/concepts/component-traits' },
{ text: 'Component Traits', link: '/learn/concepts/component-traits' },
{ text: 'Addons', link: '/learn/concepts/addons' }
]
},
{
text: "FAQ",
items: [
{ text: 'How can I contribute?', link: '/learn/faq/contributing' }
{ text: 'Common Issues', link: '/learn/faq/common-issues' },
{ text: 'Migrating from Matter', link: '/learn/faq/migrating-from-matter' },
{ text: 'Contributing', link: '/learn/faq/contributing' }
]
},
}
],
"/contributing/": [
{
text: 'Contributing',
items: [
{ text: 'Contribution Guidelines', link: '/learn/contributing/guidelines' },
{ text: 'Submitting Issues', link: '/learn/contributing/issues' },
{ text: 'Submitting Pull Requests', link: '/learn/contributing/pull-requests' },
{ text: 'Guidelines', link: '/contributing/guidelines' },
{ text: 'Submitting Issues', link: '/contributing/issues' },
{ text: 'Pull Requests', link: '/contributing/pull-requests' }
]
}
]
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/ukendio/jecs' }
]
{ icon: 'github', link: 'https://github.com/ukendio/jecs' },
{ icon: 'discord', link: 'https://discord.gg/h2NV8PqhAD' }
],
search: {
provider: 'local',
options: {
detailedView: true
}
}
}
})

View file

@ -1,50 +1,74 @@
# Jecs
# Jecs API Reference
Jecs. Just an Entity Component System.
Jecs provides a simple but powerful API for entity component systems. This page documents the core API.
# Properties
## Core Types
## World
### World
```luau
jecs.World: World
```
A world is a container of all ECS data. Games can have multiple worlds but component IDs may conflict between worlds. Ensure to register the same component IDs in the same order for each world.
The main container for all ECS data. See [World API](world.md) for details.
## Wildcard
### Entity
```luau
type Entity<T = unknown>
```
A unique identifier that can have components attached. The generic type `T` represents the data type of the entity when used as a component.
### Id
```luau
type Id<T>
```
Represents either an Entity or a Pair that can be used to store component data of type `T`.
## Core Functions
### pair()
```luau
function jecs.pair(
first: Entity, -- The first element of the pair (relationship)
object: Entity -- The second element of the pair (target)
): number -- Returns the ID representing this relationship pair
```
Creates a relationship pair between two entities. Used for creating relationships like parent-child, ownership, etc.
::: info
Note that while relationship pairs can be used as components (meaning you can add data with them as an ID), they cannot be used as entities. You cannot add components to a pair as the source of a binding.
:::
Example:
```lua
local ChildOf = world:component()
local parent = world:entity()
local child = world:entity()
-- Create parent-child relationship
world:add(child, pair(ChildOf, parent))
```
## Constants
### Wildcard
```luau
jecs.Wildcard: Entity
```
Builtin component type. This ID is used for wildcard queries.
Special entity used for querying any entity in a relationship. See [Relationships](../learn/concepts/relationships.md).
## Component
### Component
```luau
jecs.Component: Entity
```
Builtin component type. Every ID created with [world:component()](world.md#component()) has this type added to it. This is meant for querying every component ID.
Built-in component type. Every component created with `world:component()` has this added to it.
## ChildOf
### ChildOf
```luau
jecs.ChildOf: Entity
```
Builtin component type. This ID is for creating parent-child hierarchies.
Built-in relationship type for parent-child hierarchies.
## Rest
### Rest
```luau
jecs.Rest: Entity
```
# Functions
## pair()
```luau
function jecs.pair(
first: Entity, -- The first element of the pair, referred to as the relationship of the relationship pair.
object: Entity, -- The second element of the pair, referred to as the target of the relationship pair.
): number -- Returns the ID with those two elements
```
::: info
Note that while relationship pairs can be used as components, meaning you can add data with it as an ID, however they cannot be used as entities. Meaning you cannot add components to a pair as the source of a binding.
:::
Special component used in queries to match remaining components.

View file

@ -1,87 +1,94 @@
# Query
# Query API
A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components.
Queries allow you to efficiently find and iterate over entities that have a specific set of components.
# Methods
## iter
Returns an iterator that can be used to iterate over the query.
## Methods
### iter()
```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
```
Returns an iterator that yields matching entities and their component values.
Example:
::: code-group
```luau [luau]
for id, position, velocity in world:query(Position, Velocity):iter() do
-- Do something with position and velocity
end
```
```typescript [typescript]
for (const [id, position, velocity] of world.query(Position, Velocity)) {
// Do something with position and velocity
}
```
:::
### with()
```luau
function Query:with(...: Entity): Query
```
Adds components to filter by, but doesn't return their values in iteration. Useful for filtering by tags.
Example:
::: code-group
```luau [luau]
-- Only get position for entities that also have Velocity
for id, position in world:query(Position):with(Velocity) do
-- Do something
-- Do something with position
end
```
```ts [typescript]
```typescript [typescript]
for (const [id, position] of world.query(Position).with(Velocity)) {
// Do something
// Do something with position
}
```
:::
:::info
Put the IDs inside of `world:query()` instead if you need the data.
:::
## without
Removes entities with the provided components from the query.
### without()
```luau
function Query:without(
...: Entity -- The IDs to filter against.
): Query -- Returns the Query
function Query:without(...: Entity): Query
```
Excludes entities that have any of the specified components.
Example:
::: code-group
```luau [luau]
for entity, position in world:query(Position):without(Velocity) do
-- Do something
-- Get position for entities that don't have Velocity
for id, position in world:query(Position):without(Velocity) do
-- Do something with position
end
```
```ts [typescript]
for (const [entity, position] of world.query(Position).without(Velocity)) {
// Do something
```typescript [typescript]
for (const [id, position] of world.query(Position).without(Velocity)) {
// Do something with position
}
```
:::
## archetypes
### cached()
```luau
function Query:cached(): Query
```
Returns the matching archetypes of the query.
Returns a cached version of the query for better performance when using the same query multiple times.
### archetypes()
```luau
function Query:archetypes(): { Archetype }
```
Example:
Returns the matching archetypes for low-level query customization.
```luau [luau]
:::info
This method is for advanced users who need fine-grained control over query behavior at the archetype level.
:::
Example:
```luau
for i, archetype in world:query(Position, Velocity):archetypes() do
local columns = archetype.columns
local field = archetype.records
@ -96,15 +103,3 @@ for i, archetype in world:query(Position, Velocity):archetypes() do
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,35 +1,137 @@
# World
# World API
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.
## Constructor
### new()
```luau
function World.new(): World
```
Creates a new World instance. Multiple worlds can exist simultaneously, but component IDs may conflict between worlds.
Example:
::: code-group
```luau [luau]
local world = jecs.World.new()
local myOtherWorld = jecs.World.new()
local otherWorld = jecs.World.new()
```
```ts [typescript]
import { World } from "@rbxts/jecs";
```typescript [typescript]
const world = new World();
const myOtherWorld = new World();
const otherWorld = new World();
```
:::
## Entity Operations
### entity()
```luau
function World:entity(): Entity
```
Creates a new entity with no components.
Example:
::: code-group
```luau [luau]
local entity = world:entity()
```
```typescript [typescript]
const entity = world.entity();
```
:::
### component()
```luau
function World:component<T>(): Entity<T>
```
Creates a new component type. Components are entities that can have other components added to them.
Example:
::: code-group
```luau [luau]
local Health = world:component() :: jecs.Entity<number>
local Position = world:component() :: jecs.Entity<Vector3>
```
```typescript [typescript]
const Health = world.component<number>();
const Position = world.component<Vector3>();
```
:::
### delete()
```luau
function World:delete<T>(id: Entity<T>)
```
Deletes an entity and all its components/relationships.
## Component Operations
### add()
```luau
function World:add<T, U>(id: Entity<T>, component: Id<U>)
```
Adds a component to an entity without setting a value. Useful for tags or components with default values.
### set()
```luau
function World:set<T, U>(id: Entity<T>, component: Id<U>, value: U)
```
Sets a component's value on an entity. Will add the component if it doesn't exist.
### get()
```luau
function World:get<T>(entity: Entity, id: Entity<T>): T?
```
Gets the value of a component on an entity. Returns nil if the component doesn't exist.
### remove()
```luau
function World:remove<T, U>(id: Entity<T>, component: Id<U>)
```
Removes a component from an entity.
### clear()
```luau
function World:clear<T>(id: Entity<T>)
```
Removes all components from an entity without deleting the entity.
## Relationship Operations
### target()
```luau
function World:target<T, U>(id: Entity<T>, relation: Entity<U>, index?: number): Entity?
```
Gets the target entity of a relationship. For example, getting the parent entity in a ChildOf relationship.
## Query Operations
### query()
```luau
function World:query(...: Entity): Query
```
Creates a new Query to find entities with specific components. See [Query API](query.md) for details.
## Maintenance
### cleanup()
```luau
function World:cleanup()
```
Cleans up the world by removing empty archetypes and rebuilding archetype collections. Helps maintain memory efficiency.
# Methods
## entity

View file

@ -1,3 +1,91 @@
## TODO
# Contributing to Jecs
This is a TODO stub.
Thank you for your interest in contributing to Jecs! This document will help you get started with contributing to the project.
## Code of Conduct
We expect all contributors to follow our Code of Conduct. Please be respectful and professional in all interactions.
## Ways to Contribute
There are many ways to contribute to Jecs:
1. **Bug Reports**: Report bugs through [GitHub Issues](https://github.com/ukendio/jecs/issues)
2. **Feature Requests**: Suggest new features or improvements
3. **Documentation**: Help improve or translate documentation
4. **Code Contributions**: Submit pull requests for bug fixes or features
5. **Examples**: Create example projects using Jecs
## Development Setup
1. Fork and clone the repository:
```bash
git clone https://github.com/your-username/jecs.git
cd jecs
```
2. Install dependencies:
```bash
# Using Wally (Luau)
wally install
# Using npm (TypeScript)
npm install
```
3. Run tests:
```bash
# Luau tests
luau test/tests.luau
```
## Code Style
- Follow existing code style and formatting
- Use clear, descriptive variable and function names
- Add comments for complex logic
- Include type annotations
- Write tests for new features
## Commit Messages
- Use clear, descriptive commit messages
- Start with a verb in present tense (e.g., "Add", "Fix", "Update")
- Reference issue numbers when applicable
Example:
```
Add relationship query caching (#123)
- Implement query result caching for relationship queries
- Add cache invalidation on component changes
- Update documentation with caching examples
```
## Documentation
When adding new features or making changes:
1. Update relevant API documentation
2. Add examples demonstrating usage
3. Update type definitions if necessary
4. Consider adding performance benchmarks for significant changes
## Testing
- Add tests for new features
- Ensure all tests pass before submitting PR
- Include performance tests for performance-critical code
- Test both Luau and TypeScript implementations
## Getting Help
If you need help:
- Join our [Discord server](https://discord.gg/h2NV8PqhAD)
- Ask questions in GitHub Discussions
- Check existing issues and documentation
## License
By contributing to Jecs, you agree that your contributions will be licensed under the MIT License.

View file

@ -1,3 +1,84 @@
## TODO
# Submitting Issues
This is a TODO stub.
When submitting issues for Jecs, please follow these guidelines to help us understand and address your concerns effectively.
## Issue Types
We use different templates for different types of issues:
1. **Bug Reports**: For reporting bugs or unexpected behavior
2. **Feature Requests**: For suggesting new features or improvements
3. **Documentation**: For documentation-related issues
## Before Submitting
1. Search existing issues to avoid duplicates
2. Check the documentation to ensure the behavior isn't intended
3. Try the latest version to see if the issue persists
## Bug Reports
When submitting a bug report:
1. Use the bug report template
2. Include a clear description of the problem
3. Provide reproduction steps
4. Include code examples
5. Specify your environment:
- Jecs version
- Roblox version
- Platform (Luau/TypeScript)
Example bug report:
```markdown
**Description**
Query iterator crashes when using relationship components
**Steps to Reproduce**
1. Create a world
2. Add relationship components
3. Query for relationships
4. Iterate results
**Code Example**
```lua
local world = jecs.World.new()
local ChildOf = world:component()
-- ... rest of reproduction code
```
**Expected Behavior**
Query should iterate through all matching entities
**Actual Behavior**
Query crashes with error: ...
```
## Feature Requests
When requesting features:
1. Use the feature request template
2. Explain the use case
3. Provide example usage
4. Consider implementation details
5. Discuss alternatives considered
## Documentation Issues
For documentation issues:
1. Specify the affected documentation section
2. Explain what needs to be changed
3. Provide suggested improvements
4. Include examples if relevant
## Labels
Common issue labels:
- `bug`: Bug reports
- `enhancement`: Feature requests
- `documentation`: Documentation issues
- `good first issue`: Good for newcomers
- `help wanted`: Extra attention needed

View file

@ -1,3 +1,103 @@
## TODO
# Submitting Pull Requests
This is a TODO stub.
This guide will help you submit effective pull requests to Jecs.
## Before Creating a PR
1. Create an issue for discussion if one doesn't exist
2. Fork the repository
3. Create a feature branch
4. Ensure tests pass
5. Update documentation
## PR Guidelines
### Branch Naming
Use descriptive branch names:
- `feature/description`
- `fix/issue-description`
- `docs/update-section`
Example: `feature/add-relationship-caching`
### PR Description
Use the PR template and include:
1. Brief description of changes
2. Link to related issues
3. Breaking changes (if any)
4. Testing performed
5. Documentation updates
Example:
```markdown
## Description
Add caching support for relationship queries
Fixes #123
## Changes
- Implement query result caching
- Add cache invalidation
- Update documentation
## Breaking Changes
None
## Testing
- Added unit tests
- Performed performance benchmarks
- Tested with example project
```
### Code Review Process
1. Submit draft PR early for feedback
2. Address review comments
3. Keep commits focused and clean
4. Rebase on main when needed
5. Ensure CI passes
### Testing Requirements
- Add/update unit tests
- Test both Luau and TypeScript
- Include performance tests if relevant
- Test documentation examples
### Documentation
Update documentation:
1. API references
2. Examples
3. Type definitions
4. Performance notes
### Final Checklist
Before marking PR as ready:
- [ ] Tests pass
- [ ] Documentation updated
- [ ] Code follows style guide
- [ ] Commits are clean
- [ ] PR description complete
- [ ] Changes reviewed locally
## After Submission
1. Respond to review comments
2. Keep PR updated with main
3. Help with integration testing
4. Update based on feedback
## Getting Help
Need help with your PR?
- Ask in Discord
- Comment on the PR
- Tag maintainers if stuck

View file

@ -1,6 +1,111 @@
# Component Traits
Component traits are IDs and pairs that can be added to components to modify their behavior. Although it is possible to create custom traits, this manual only contains an overview of all builtin component traits supported by Jecs.
Component traits in Jecs allow you to modify component behavior through special IDs and pairs. They provide a way to configure how components interact with entities and the world.
## Built-in Traits
### Component
```lua
jecs.Component
```
Identifies an ID as a component. Every component created with `world:component()` automatically has this trait.
### Tag
```lua
jecs.Tag
```
Marks a component as a tag that never contains data. This improves performance for structural changes.
## Cleanup Traits
Cleanup traits define what happens when entities used as components or relationship targets are deleted.
### OnDelete
Specifies what happens when a component or relationship is deleted:
::: code-group
```lua [luau]
local Archer = world:component()
world:add(Archer, pair(jecs.OnDelete, jecs.Remove))
local entity = world:entity()
world:add(entity, Archer)
-- This will remove Archer from entity
world:delete(Archer)
```
```typescript [typescript]
const Archer = world.component();
world.add(Archer, pair(jecs.OnDelete, jecs.Remove));
const entity = world.entity();
world.add(entity, Archer);
// This will remove Archer from entity
world.delete(Archer);
```
:::
### OnDeleteTarget
Specifies what happens when a relationship target is deleted:
```lua
local OwnedBy = world:component()
world:add(OwnedBy, pair(jecs.OnDeleteTarget, jecs.Remove))
local item = world:entity()
local player = world:entity()
world:add(item, pair(OwnedBy, player))
-- This will remove (OwnedBy, player) from item
world:delete(player)
```
## Cleanup Actions
Two cleanup actions are available:
1. **Remove**: Removes instances of the specified component/relationship (default)
2. **Delete**: Deletes all entities with the specified component/relationship
## Example Usage
### Tag Components
```lua
local IsEnemy = world:component()
world:add(IsEnemy, jecs.Tag)
-- More efficient than storing a boolean
world:add(entity, IsEnemy)
```
### Cleanup Configuration
```lua
-- Delete children when parent is deleted
local ChildOf = world:component()
world:add(ChildOf, pair(jecs.OnDeleteTarget, jecs.Delete))
-- Remove ownership when owner is deleted
local OwnedBy = world:component()
world:add(OwnedBy, pair(jecs.OnDeleteTarget, jecs.Remove))
```
## Best Practices
1. **Use Tags Appropriately**
- Use tags for boolean-like components
- Configure tags early in component setup
- Document tag usage
2. **Cleanup Configuration**
- Consider cleanup behavior during design
- Document cleanup policies
- Test cleanup behavior
3. **Performance Considerations**
- Use tags for better performance
- Configure cleanup for efficient entity management
- Consider relationship cleanup impact
# Component

View file

@ -1,120 +1,139 @@
# Entities and Components
## Entities
## What are Entities?
Entities represent things in a game. In a game there may be entities of characters, buildings, projectiles, particle effects etc.
Entities are the fundamental building blocks in Jecs. An entity represents any object in your game - a character, a building, a projectile, or even abstract concepts like game rules or spawn points.
By itself, an entity is just an unique identifier without any data
By itself, an entity is just a unique identifier (a number) without any data. Entities become useful when you add components to them.
## Components
## What are 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").
Components are reusable pieces of data that can be attached to entities. They serve three main purposes:
## Operations
1. **Data Storage**: Hold data for an entity (e.g., Position, Health)
2. **Tagging**: Mark an entity as having certain properties (e.g., IsPlayer, IsEnemy)
3. **Relationships**: Create connections between entities (e.g., ChildOf, Owns)
| 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 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]
### Example Components
```lua
-- Data component
local Position = world:component() :: jecs.Entity<Vector3>
print(world:has(Position, jecs.Component))
local Health = world:component() :: jecs.Entity<number>
-- Tag component
local IsEnemy = world:component()
-- Relationship component
local ChildOf = world:component()
```
```typescript [typescript]
const Position = world.component<Vector3>();
print(world.has(Position, jecs.Component));
```
## Component Operations
:::
Jecs provides several operations for working with components:
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
| Operation | Description | Example |
|-----------|------------------------------------------------------------|-|
| `add` | Adds a component to an entity (no value) | `world:add(entity, IsEnemy)` |
| `set` | Sets a component's value on an entity | `world:set(entity, Health, 100)` |
| `get` | Gets a component's value from an entity | `local health = world:get(entity, Health)` |
| `remove` | Removes a component from an entity | `world:remove(entity, IsEnemy)` |
| `clear` | Removes all components from an entity | `world:clear(entity)` |
### Example Usage
::: code-group
```lua [luau]
local world = jecs.World.new()
```luau [luau]
local Networked = world:component()
local Type = world:component()
local Name = world:component()
-- Create components
local Position = world:component() :: jecs.Entity<Vector3>
world:add(Position, Networked)
world:set(Position, Name, "Position")
world:set(Position, Type, { size = 12, type = "Vector3" } ) -- 12 bytes to represent a Vector3
local Health = world:component() :: jecs.Entity<number>
local IsEnemy = world:component()
for id, ty, name in world:query(Type, Name, 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
-- Create an entity
local enemy = world:entity()
updatePositions:FireServer(packet)
-- Add components and data
world:set(enemy, Position, Vector3.new(0, 0, 0))
world:set(enemy, Health, 100)
world:add(enemy, IsEnemy)
-- Get component data
local pos = world:get(enemy, Position)
print(`Enemy position: {pos}`)
-- Check if entity has component
if world:has(enemy, IsEnemy) then
print("This is an enemy!")
end
```
```typescript [typescript]
const Networked = world.component();
const Type = world.component();
const Name = world.component();
const world = new World();
// Create components
const Position = world.component<Vector3>();
world.add(Position, Networked);
world.set(Position, Name, "Position");
world.set(Position, Type, { size: 12, type: "Vector3" }); // 12 bytes to represent a Vector3
const Health = world.component<number>();
const IsEnemy = world.component();
for (const [id, ty, name] of world.query(Type, Name, Networked)) {
const batch = new Array<{ entity: Entity; data: unknown }>();
// Create an entity
const enemy = world.entity();
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);
}
}
// Add components and data
world.set(enemy, Position, new Vector3(0, 0, 0));
world.set(enemy, Health, 100);
world.add(enemy, IsEnemy);
updatePositions.FireServer(packet);
// Get component data
const pos = world.get(enemy, Position);
print(`Enemy position: ${pos}`);
// Check if entity has component
if (world.has(enemy, IsEnemy)) {
print("This is an enemy!");
}
```
:::
## Components are Entities
In Jecs, components themselves are entities with a special `Component` component. This means you can add components to components! This enables powerful features like:
1. Adding metadata to components
2. Creating component hierarchies
3. Defining component relationships
Example:
```lua
local Position = world:component() :: jecs.Entity<Vector3>
local Networked = world:component()
local Type = world:component()
-- Add metadata to Position component
world:add(Position, Networked)
world:set(Position, Type, { size = 12, type = "Vector3" })
```
## Best Practices
1. **Keep Components Simple**
- Components should store related data
- Avoid complex nested structures
- Use multiple components instead of one complex component
2. **Use Tags Effectively**
- Tags are components without data
- Great for marking entity states or categories
- More efficient than components with boolean values
3. **Component Naming**
- Use clear, descriptive names
- Consider using noun-based names for data (Position, Health)
- Consider using adjective-based names for tags (IsEnemy, IsDead)
4. **Data Organization**
- Group related data in components
- Split unrelated data into separate components
- Use relationships for entity connections
## Singletons
Singletons are components for which only a single instance

View file

@ -1,5 +1,147 @@
# Queries
Queries are the primary way to find and iterate over entities with specific components in Jecs. They provide an efficient way to process only the entities you care about.
## Basic Queries
A basic query finds all entities that have a specific set of components:
::: code-group
```lua [luau]
local Position = world:component() :: jecs.Entity<Vector3>
local Velocity = world:component() :: jecs.Entity<Vector3>
-- Find all entities with both Position and Velocity
for id, position, velocity in world:query(Position, Velocity) do
-- Update position based on velocity
world:set(id, Position, position + velocity)
end
```
```typescript [typescript]
const Position = world.component<Vector3>();
const Velocity = world.component<Vector3>();
// Find all entities with both Position and Velocity
for (const [id, position, velocity] of world.query(Position, Velocity)) {
// Update position based on velocity
world.set(id, Position, position.add(velocity));
}
```
:::
## Query Filters
Queries can be refined using filters to be more specific about what entities you want:
### with()
Find entities that have additional components, but don't need their values:
```lua
-- Find entities with Position that also have IsEnemy
for id, position in world:query(Position):with(IsEnemy) do
-- Process enemy positions
end
```
### without()
Exclude entities that have specific components:
```lua
-- Find entities with Position that don't have IsDestroyed
for id, position in world:query(Position):without(IsDestroyed) do
-- Process active entities
end
```
## Query Performance
Queries in Jecs are optimized for performance:
1. **Archetype-based**: Entities are grouped by their component combinations
2. **Cache-friendly**: Components are stored in contiguous memory
3. **Zero Allocation**: Iteration doesn't allocate memory
### Query Caching
For frequently used queries, you can cache them to avoid rebuilding the query:
```lua
-- Cache a commonly used query
local movementQuery = world:query(Position, Velocity):cached()
-- Use the cached query
for id, position, velocity in movementQuery:iter() do
-- Process movement
end
```
## Advanced Query Features
### Relationship Queries
Query entities based on their relationships:
```lua
local ChildOf = world:component()
-- Find all children of a parent
for child in world:query(pair(ChildOf, parent)) do
-- Process child entities
end
-- Find entities with any parent
for child in world:query(pair(ChildOf, jecs.Wildcard)) do
local parent = world:target(child, ChildOf)
-- Process parent-child relationship
end
```
### Component Trait Queries
Find components with specific traits:
```lua
-- Find all networked components
for component in world:query(jecs.Component):with(Networked) do
-- Process networked components
end
```
## Best Practices
1. **Query Organization**
- Keep queries focused on related components
- Cache frequently used queries
- Consider splitting complex queries into simpler ones
2. **Performance Optimization**
- Use `with()` for components you don't need values from
- Consider component order (most restrictive first)
- Cache queries used in hot paths
3. **Query Design**
- Query only the components you need
- Use relationships for hierarchical queries
- Consider using tags for efficient filtering
4. **Common Patterns**
```lua
-- Movement system
for id, pos, vel in world:query(Position, Velocity):without(Frozen) do
world:set(id, Position, pos + vel)
end
-- Damage system
for id, health in world:query(Health):with(TakingDamage) do
if health <= 0 then
world:add(id, IsDead)
end
end
-- Cleanup system
for id in world:query(IsDead) do
world:delete(id)
end
```
## Introductiuon
Queries enable games to quickly find entities that satifies provided conditions.
@ -9,7 +151,7 @@ Jecs queries can do anything from returning entities that match a simple list of
This manual contains a full overview of the query features available in Jecs. Some of the features of Jecs queries are:
- Queries have support for relationships pairs which allow for matching against entity graphs without having to build complex data structures for it.
- Queries support filters such as [`query:with(...)`](../../api/query.md#with) if entities are required to have the components but you dont actually care about components value. And [`query:without(...)`](../../api/query.md#without) which selects entities without the components.
- Queries support filters such as [`query:with(...)`](../../api/query.md#with) if entities are required to have the components but you don't actually care about components value. And [`query:without(...)`](../../api/query.md#without) which selects entities without the components.
- Queries can be drained or reset on when called, which lets you choose iterator behaviour.
- Queries can be called with any ID, including entities created dynamically, this is useful for pairs.
- Queries are already fast but can be futher inlined via [`query:archetypes()`](../../api/query.md#archetypes) for maximum performance to eliminate function call overhead which is roughly 70-80% of the cost for iteration.

View file

@ -1,9 +1,114 @@
# Relationships
Relationships makes it possible to describe entity graphs natively in ECS.
Adding/removing relationships is similar to adding/removing regular components, with as difference that instead of a single component id, a relationship adds a pair of two things to an entity. In this pair, the first element represents the relationship (e.g. "Eats"), and the second element represents the relationship target (e.g. "Apples").
Relationships in Jecs allow you to create connections between entities using pairs. This enables modeling hierarchies, ownership, and other entity associations.
Relationships can be used to describe many things, from hierarchies to inventory systems to trade relationships between players in a game. The following sections go over how to use relationships, and what features they support.
## Basic Relationships
A relationship is created using pairs:
::: code-group
```lua [luau]
local world = jecs.World.new()
local ChildOf = world:component()
local parent = world:entity()
local child = world:entity()
-- Create parent-child relationship
world:add(child, pair(ChildOf, parent))
```
```typescript [typescript]
const world = new World();
const ChildOf = world.component();
const parent = world.entity();
const child = world.entity();
// Create parent-child relationship
world.add(child, pair(ChildOf, parent));
```
:::
## Relationship Types
Jecs supports several types of relationships:
### Parent-Child
Built-in `ChildOf` relationship for hierarchies:
```lua
-- Using built-in ChildOf
world:add(child, pair(jecs.ChildOf, parent))
-- Get parent
local parent = world:parent(child)
```
### Custom Relationships
Create your own relationship types:
```lua
local OwnedBy = world:component()
local Likes = world:component()
-- Create relationships
world:add(item, pair(OwnedBy, player))
world:add(bob, pair(Likes, alice))
```
## Querying Relationships
Find entities with specific relationships:
```lua
-- Find all children of a parent
for child in world:query(pair(ChildOf, parent)) do
-- Process child
end
-- Find entities with any parent
for child in world:query(pair(ChildOf, jecs.Wildcard)) do
local parent = world:target(child, ChildOf)
-- Process child and parent
end
```
## Relationship Data
Relationships can store data:
```lua
local Damage = world:component()
-- Store damage amount in relationship
world:set(weapon, pair(Damage, target), 50)
-- Get damage amount
local damage = world:get(weapon, pair(Damage, target))
```
## Best Practices
1. **Relationship Design**
- Use clear relationship names
- Consider relationship directionality
- Document relationship semantics
2. **Performance**
- Cache frequently used relationship queries
- Be mindful of relationship complexity
- Use built-in relationships when possible
3. **Common Patterns**
```lua
-- Hierarchy traversal
for child in world:children(parent) do
-- Process children
end
-- Relationship queries
for entity, damage in world:query(pair(Damage, target)) do
-- Process damage
end
```
## Definitions

View file

@ -0,0 +1,116 @@
# Common Issues
This guide covers common issues you might encounter when using Jecs and their solutions.
## Performance Issues
### Query Performance
**Issue**: Queries are running slower than expected
**Solutions**:
1. Cache frequently used queries:
```lua
local movementQuery = world:query(Position, Velocity):cached()
```
2. Use `with()` for components you don't need values from:
```lua
-- Instead of
for id, pos, _ in world:query(Position, IsEnemy) do end
-- Use
for id, pos in world:query(Position):with(IsEnemy) do end
```
3. Minimize relationship complexity to reduce archetype fragmentation
### Memory Usage
**Issue**: High memory usage with many entities
**Solutions**:
1. Use tags instead of boolean components
2. Call `world:cleanup()` periodically to remove empty archetypes
3. Consider component data structure size
## Type Issues
### Component Type Errors
**Issue**: Type errors with components
**Solutions**:
1. Always specify component types:
```lua
local Health = world:component() :: jecs.Entity<number>
local Position = world:component() :: jecs.Entity<Vector3>
```
2. Use correct types in set operations:
```lua
-- Correct
world:set(entity, Position, Vector3.new(0, 0, 0))
-- Wrong
world:set(entity, Position, {x=0, y=0, z=0})
```
## Relationship Issues
### Missing Targets
**Issue**: Cannot find relationship targets
**Solutions**:
1. Use `world:target()` to get relationship targets:
```lua
local parent = world:target(entity, ChildOf)
```
2. Check relationship existence:
```lua
if world:has(entity, pair(ChildOf, parent)) then
-- Process relationship
end
```
### Cleanup Issues
**Issue**: Entities not cleaning up properly
**Solutions**:
1. Configure cleanup traits:
```lua
world:add(ChildOf, pair(jecs.OnDeleteTarget, jecs.Delete))
```
2. Manually clean up relationships before deleting entities:
```lua
world:clear(entity) -- Remove all components
world:delete(entity) -- Then delete entity
```
## Best Practices
### Query Organization
- Cache frequently used queries
- Use appropriate filters (`with`/`without`)
- Consider component order in queries
### Component Design
- Keep components small and focused
- Use tags for boolean states
- Document component types
### Memory Management
- Clean up unused entities
- Configure appropriate cleanup traits
- Monitor archetype fragmentation
## Getting Help
If you encounter issues not covered here:
1. Check the [API documentation](../../api/jecs.md)
2. Join our [Discord server](https://discord.gg/h2NV8PqhAD)
3. Open an issue on [GitHub](https://github.com/ukendio/jecs/issues)

View file

@ -1,68 +1,210 @@
# First Jecs project
# Your First Jecs Project
Now that you have installed Jecs, you can create your [World](https://ukendio.github.io/jecs/api/world.html).
This tutorial will walk you through creating your first project with Jecs, demonstrating core concepts and best practices.
:::code-group
```luau [luau]
local jecs = require(path/to/jecs)
## Setting Up
First, make sure you have Jecs installed. If not, check the [Getting Started](get-started.md) guide.
::: code-group
```lua [luau]
local jecs = require(path.to.jecs)
local world = jecs.World.new()
```
```typescript [typescript]
import { World } from "@rbxts/jecs"
const world = new World()
import { World } from "@rbxts/jecs";
const world = new World();
```
:::
Let's create a couple components.
## Creating Components
:::code-group
```luau [luau]
local jecs = require(path/to/jecs)
local world = jecs.World.new()
Let's create some basic components for a simple game:
local Position = world:component()
local Velocity = world:component()
::: code-group
```lua [luau]
-- Position in 3D space
local Position = world:component() :: jecs.Entity<Vector3>
-- Velocity for movement
local Velocity = world:component() :: jecs.Entity<Vector3>
-- Health for gameplay
local Health = world:component() :: jecs.Entity<number>
-- Tag for marking enemies
local IsEnemy = world:component()
```
```typescript [typescript]
import { World } from "@rbxts/jecs"
const world = new World()
// Position in 3D space
const Position = world.component<Vector3>();
const Position = world.component()
const Velocity = world.component()
// Velocity for movement
const Velocity = world.component<Vector3>();
// Health for gameplay
const Health = world.component<number>();
// Tag for marking enemies
const IsEnemy = world.component();
```
:::
Systems can be as simple as a query in a function or a more contextualized construct. Let's make a system that moves an entity and decelerates over time.
## Creating Game Systems
:::code-group
```luau [luau]
local jecs = require(path/to/jecs)
local world = jecs.World.new()
Let's create some basic systems to handle movement and gameplay:
local Position = world:component()
local Velocity = world:component()
### Movement System
::: code-group
```lua [luau]
-- Cache the query for better performance
local movementQuery = world:query(Position, Velocity):cached()
for id, position, velocity in world:query(Position, Velocity) do
world:set(id, Position, position + velocity)
world:set(id, Velocity, velocity * 0.9)
local function updateMovement(deltaTime)
for id, position, velocity in movementQuery:iter() do
-- Update position based on velocity and time
world:set(id, Position, position + velocity * deltaTime)
end
end
```
```typescript [typescript]
import { World } from "@rbxts/jecs"
const world = new World()
// Cache the query for better performance
const movementQuery = world.query(Position, Velocity).cached();
const Position = world.component()
const Velocity = world.component()
for (const [id, position, velocity] of world.query(Position, Velocity)) {
world.set(id, Position, position.add(velocity))
world.set(id, Velocity, velocity.mul(0.9))
function updateMovement(deltaTime: number) {
for (const [id, position, velocity] of movementQuery) {
// Update position based on velocity and time
world.set(id, Position, position.add(velocity.mul(deltaTime)));
}
}
```
:::
### Damage System
::: code-group
```lua [luau]
local function applyDamage(entity, amount)
local currentHealth = world:get(entity, Health)
if currentHealth then
local newHealth = currentHealth - amount
if newHealth <= 0 then
world:delete(entity)
else
world:set(entity, Health, newHealth)
end
end
end
```
```typescript [typescript]
function applyDamage(entity: Entity, amount: number) {
const currentHealth = world.get(entity, Health);
if (currentHealth) {
const newHealth = currentHealth - amount;
if (newHealth <= 0) {
world.delete(entity);
} else {
world.set(entity, Health, newHealth);
}
}
}
```
:::
## Creating Game Entities
Now let's create some game entities:
::: code-group
```lua [luau]
-- Create a player
local player = world:entity()
world:set(player, Position, Vector3.new(0, 0, 0))
world:set(player, Velocity, Vector3.new(0, 0, 0))
world:set(player, Health, 100)
-- Create an enemy
local enemy = world:entity()
world:set(enemy, Position, Vector3.new(10, 0, 10))
world:set(enemy, Health, 50)
world:add(enemy, IsEnemy)
```
```typescript [typescript]
// Create a player
const player = world.entity();
world.set(player, Position, new Vector3(0, 0, 0));
world.set(player, Velocity, new Vector3(0, 0, 0));
world.set(player, Health, 100);
// Create an enemy
const enemy = world.entity();
world.set(enemy, Position, new Vector3(10, 0, 10));
world.set(enemy, Health, 50);
world.add(enemy, IsEnemy);
```
:::
## Adding Relationships
Let's add some parent-child relationships:
::: code-group
```lua [luau]
-- Create weapon entity
local weapon = world:entity()
world:add(weapon, pair(jecs.ChildOf, player))
-- Query for player's children
for child in world:query(pair(jecs.ChildOf, player)) do
print("Found player's child:", child)
end
```
```typescript [typescript]
// Create weapon entity
const weapon = world.entity();
world.add(weapon, pair(jecs.ChildOf, player));
// Query for player's children
for (const [child] of world.query(pair(jecs.ChildOf, player))) {
print("Found player's child:", child);
}
```
:::
## Running the Game Loop
Here's how to put it all together:
::: code-group
```lua [luau]
local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function(deltaTime)
-- Update movement
updateMovement(deltaTime)
-- Other game systems...
end)
```
```typescript [typescript]
const RunService = game.GetService("RunService");
RunService.Heartbeat.Connect((deltaTime) => {
// Update movement
updateMovement(deltaTime);
// Other game systems...
});
```
:::
## Next Steps
1. Learn more about [Entities and Components](../concepts/entities-and-components.md)
2. Explore [Queries](../concepts/queries.md) in depth
3. Understand [Relationships](../concepts/relationships.md)
4. Check out [Component Traits](../concepts/component-traits.md)
5. Browse the [API Reference](../../api/jecs.md)
## Where To Get Help
If you are encountering problems, there are resources for you to get help:

View file

@ -1,29 +1,29 @@
# Getting Started
# Getting Started with Jecs
This guide will help you get Jecs up and running in your project.
## Installation
### Installing Standalone
Choose your preferred installation method:
Navigate to the [releases page](https://github.com/Ukendio/jecs/releases) and download `jecs.rbxm` from the assets.
![jecs.rbxm](rbxm.png)
### Using Wally
Add the following to your wally configuration:
### Using Wally (Luau)
Add to your wally.toml:
::: code-group
```toml [wally.toml]
[dependencies]
jecs = "ukendio/jecs@0.2.3"
```
:::
Then run:
```bash
wally install
```
### Using npm (roblox-ts)
Use one of the following commands on your root project directory:
Use your preferred package manager:
::: code-group
```bash [npm]
npm i https://github.com/Ukendio/jecs.git
@ -34,97 +34,166 @@ yarn add https://github.com/Ukendio/jecs.git
```bash [pnpm]
pnpm add https://github.com/Ukendio/jecs.git
```
:::
## Example Usage
### Standalone Installation
1. Navigate to the [releases page](https://github.com/Ukendio/jecs/releases)
2. Download `jecs.rbxm` from the assets
3. Import into your Roblox project
![jecs.rbxm](rbxm.png)
## Basic Usage
Here's a simple example to get you started:
::: code-group
```lua [luau]
local jecs = require(path.to.jecs)
```luau [Luau]
-- Create a world
local world = jecs.World.new()
-- Define components
local Position = world:component() :: jecs.Entity<Vector3>
local Velocity = world:component() :: jecs.Entity<Vector3>
-- Create an entity
local entity = world:entity()
-- Add components
world:set(entity, Position, Vector3.new(0, 0, 0))
world:set(entity, Velocity, Vector3.new(1, 0, 0))
-- Update system
local function updatePositions()
for id, position, velocity in world:query(Position, Velocity) do
world:set(id, Position, position + velocity)
end
end
```
```typescript [typescript]
import { World } from "@rbxts/jecs";
// Create a world
const world = new World();
// Define components
const Position = world.component<Vector3>();
const Velocity = world.component<Vector3>();
// Create an entity
const entity = world.entity();
// Add components
world.set(entity, Position, new Vector3(0, 0, 0));
world.set(entity, Velocity, new Vector3(1, 0, 0));
// Update system
function updatePositions() {
for (const [id, position, velocity] of world.query(Position, Velocity)) {
world.set(id, Position, position.add(velocity));
}
}
```
:::
## Advanced Example: Relationships
Here's an example showing Jecs' relationship features:
::: code-group
```lua [luau]
local world = jecs.World.new()
local pair = jecs.pair
local Wildcard = jecs.Wildcard
-- Define components
local Name = world:component()
local function getName(e)
return world:get(e, Name)
end
local Eats = world:component()
-- Relationship objects
-- Create food types (components are entities!)
local Apples = world:component()
-- components are entities, so you can add components to components
world:set(Apples, Name, "apples")
local Oranges = world:component()
world:set(Oranges, Name, "oranges")
-- Create entities with relationships
local bob = world:entity()
-- Pairs can be constructed from two entities
world:set(bob, Name, "bob")
world:set(bob, pair(Eats, Apples), 10)
world:set(bob, pair(Eats, Oranges), 5)
world:set(bob, Name, "bob")
local alice = world:entity()
world:set(alice, pair(Eats, Apples), 4)
world:set(alice, Name, "alice")
world:set(alice, pair(Eats, Apples), 4)
-- Query relationships
for id, amount in world:query(pair(Eats, Wildcard)) do
-- get the second target of the pair
local food = world:target(id, Eats)
print(string.format("%s eats %d %s", getName(id), amount, getName(food)))
print(string.format("%s eats %d %s",
world:get(id, Name),
amount,
world:get(food, Name)))
end
-- Output:
-- bob eats 10 apples
-- bob eats 5 pears
-- alice eats 4 apples
-- bob eats 5 oranges
-- alice eats 4 apples
```
:::
## Key Features
### Entity Management
```lua
-- Create entities
local entity = world:entity()
-- Delete entities
world:delete(entity)
-- Clear all components
world:clear(entity)
```
### Component Operations
```lua
-- Add component (tag)
world:add(entity, IsEnemy)
```ts [Typescript]
import { Wildcard, pair, World } from "@rbxts/jecs"
-- Set component value
world:set(entity, Health, 100)
-- Get component value
local health = world:get(entity, Health)
const world = new World()
const Name = world.component()
function getName(e) {
return world.get(e, Name)
}
const Eats = world.component()
// Relationship objects
const Apples = world.component()
// components are entities, so you can add components to components
world.set(Apples, Name, "apples")
const Oranges = world.component()
world.set(Oranges, Name, "oranges")
const bob = world.entity()
// Pairs can be constructed from two entities
world.set(bob, pair(Eats, Apples), 10)
world.set(bob, pair(Eats, Oranges), 5)
world.set(bob, Name, "bob")
const alice = world.entity()
world.set(alice, pair(Eats, Apples), 4)
world.set(alice, Name, "alice")
for (const [id, amount] of world.query(pair(Eats, Wildcard))) {
// get the second target of the pair
const food = world:target(id, Eats)
print(string.format("%s eats %d %s", getName(id), amount, getName(food)))
}
// Output:
// bob eats 10 apples
// bob eats 5 pears
// alice eats 4 apples
-- Remove component
world:remove(entity, Health)
```
### Relationships
```lua
-- Create parent-child relationship
world:add(child, pair(jecs.ChildOf, parent))
-- Query children
for child in world:query(pair(jecs.ChildOf, parent)) do
-- Process child entities
end
```
## Next Steps
1. Check out the [First Jecs Project](first-jecs-project.md) tutorial
2. Learn about [Entities and Components](../concepts/entities-and-components.md)
3. Explore [Queries](../concepts/queries.md) and [Relationships](../concepts/relationships.md)
4. Browse the [API Reference](../../api/jecs.md)
## Getting Help
- Join our [Discord server](https://discord.gg/h2NV8PqhAD)
- Check the [FAQ](../faq/common-issues.md)
- Report issues on [GitHub](https://github.com/ukendio/jecs/issues)

3
docs/public/robots.txt Normal file
View file

@ -0,0 +1,3 @@
User-agent: *
Allow: /
Sitemap: https://ukendio.github.io/jecs/sitemap.xml

16
docs/public/sitemap.xml Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://ukendio.github.io/jecs/</loc>
<priority>1.0</priority>
</url>
<url>
<loc>https://ukendio.github.io/jecs/learn/overview/get-started</loc>
<priority>0.9</priority>
</url>
<url>
<loc>https://ukendio.github.io/jecs/api/jecs</loc>
<priority>0.8</priority>
</url>
<!-- Add other important pages -->
</urlset>