mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Fix: #50 Updated Documentation
This commit is contained in:
parent
1b96975b53
commit
f5099d585a
19 changed files with 1634 additions and 365 deletions
113
README.md
113
README.md
|
@ -4,61 +4,84 @@
|
|||
|
||||
[](LICENSE) [](https://wally.run/package/ukendio/jecs)
|
||||
|
||||
Just a stupidly fast Entity Component System
|
||||
# Jecs - Just a Stupidly Fast ECS
|
||||
|
||||
- [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
|
||||
A high-performance Entity Component System (ECS) for Roblox games.
|
||||
|
||||
### Example
|
||||
## Features
|
||||
|
||||
- 🚀 **Blazing Fast**: Iterate over 800,000 entities at 60 frames per second
|
||||
- 🔗 **Entity Relationships**: First-class support for [entity relationships](docs/learn/concepts/relationships.md)
|
||||
- 🔒 **Type Safety**: Fully typed API for both [Luau](https://luau-lang.org/) and TypeScript
|
||||
- 📦 **Zero Dependencies**: No external dependencies required
|
||||
- ⚡ **Optimized Storage**: Cache-friendly [archetype/SoA](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) storage
|
||||
- ✅ **Battle-tested**: Rigorously [unit tested](https://github.com/Ukendio/jecs/actions/workflows/ci.yaml) for stability
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Getting Started](docs/learn/overview/get-started.md)
|
||||
- [API Reference](docs/api/jecs.md)
|
||||
- [Concepts](docs/learn/concepts/)
|
||||
- [Examples](examples/)
|
||||
- [FAQ](docs/learn/faq/common-issues.md)
|
||||
|
||||
## Quick Example
|
||||
|
||||
```lua
|
||||
local world = jecs.World.new()
|
||||
local pair = jecs.pair
|
||||
|
||||
-- These components and functions are actually already builtin
|
||||
-- but have been illustrated for demonstration purposes
|
||||
local ChildOf = world:component()
|
||||
local Name = world:component()
|
||||
-- Define components
|
||||
local Position = world:component() :: jecs.Entity<Vector3>
|
||||
local Velocity = world:component() :: jecs.Entity<Vector3>
|
||||
|
||||
local function parent(entity)
|
||||
return world:target(entity, ChildOf)
|
||||
-- Create an entity
|
||||
local entity = world:entity()
|
||||
world:set(entity, Position, Vector3.new(0, 0, 0))
|
||||
world:set(entity, Velocity, Vector3.new(1, 0, 0))
|
||||
|
||||
-- Update system
|
||||
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
|
||||
|
||||
local alice = world:entity()
|
||||
world:set(alice, Name, "alice")
|
||||
|
||||
local bob = world:entity()
|
||||
world:add(bob, pair(ChildOf, alice))
|
||||
world:set(bob, Name, "bob")
|
||||
|
||||
local sara = world:entity()
|
||||
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
|
||||
|
||||
-- Output
|
||||
-- "alice"
|
||||
-- bob is the child of alice
|
||||
-- sara is the child of alice
|
||||
```
|
||||
|
||||
21,000 entities 125 archetypes 4 random components queried.
|
||||

|
||||
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:
|
||||

|
||||
|
||||
### Insertion Performance
|
||||
Inserting 8 components to an entity and updating them over 50 times:
|
||||

|
||||
Can be found under /benches/visual/insertions.luau
|
||||
|
||||
## Installation
|
||||
|
||||
### Using Wally
|
||||
```toml
|
||||
[dependencies]
|
||||
jecs = "ukendio/jecs@0.2.3"
|
||||
```
|
||||
|
||||
### Using npm (roblox-ts)
|
||||
```bash
|
||||
npm i @rbxts/jecs
|
||||
```
|
||||
|
||||
### Standalone
|
||||
Download `jecs.rbxm` from our [releases page](https://github.com/Ukendio/jecs/releases).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Please see our [contribution guidelines](docs/contributing/guidelines.md) for details.
|
||||
|
||||
## Community & Support
|
||||
|
||||
- [Discord Community](https://discord.gg/h2NV8PqhAD)
|
||||
- [GitHub Issues](https://github.com/ukendio/jecs/issues)
|
||||
- [API Documentation](https://ukendio.github.io/jecs/)
|
||||
|
||||
## License
|
||||
|
||||
Jecs is [MIT licensed](LICENSE).
|
||||
|
|
8
docs/.vitepress/404.md
Normal file
8
docs/.vitepress/404.md
Normal 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)
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 don’t 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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
116
docs/learn/faq/common-issues.md
Normal file
116
docs/learn/faq/common-issues.md
Normal 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)
|
122
docs/learn/faq/migrating-from-matter.md
Normal file
122
docs/learn/faq/migrating-from-matter.md
Normal file
|
@ -0,0 +1,122 @@
|
|||
# Migrating from Matter
|
||||
|
||||
This guide helps you migrate your code from Matter ECS to Jecs.
|
||||
|
||||
## Key Differences
|
||||
|
||||
### World Creation
|
||||
```lua
|
||||
-- Matter
|
||||
local world = Matter.World.new()
|
||||
|
||||
-- Jecs
|
||||
local world = jecs.World.new()
|
||||
```
|
||||
|
||||
### Component Definition
|
||||
```lua
|
||||
-- Matter
|
||||
local Position = { name = "Position" }
|
||||
local Velocity = { name = "Velocity" }
|
||||
|
||||
-- Jecs
|
||||
local Position = world:component() :: jecs.Entity<Vector3>
|
||||
local Velocity = world:component() :: jecs.Entity<Vector3>
|
||||
```
|
||||
|
||||
### Entity Creation
|
||||
```lua
|
||||
-- Matter
|
||||
local entity = world:spawn()
|
||||
|
||||
-- Jecs
|
||||
local entity = world:entity()
|
||||
```
|
||||
|
||||
### Adding Components
|
||||
```lua
|
||||
-- Matter
|
||||
world:insert(entity, Position, { x = 0, y = 0, z = 0 })
|
||||
|
||||
-- Jecs
|
||||
world:set(entity, Position, Vector3.new(0, 0, 0))
|
||||
```
|
||||
|
||||
### Querying
|
||||
```lua
|
||||
-- Matter
|
||||
for id, pos, vel in world:query(Position, Velocity) do
|
||||
-- Process entities
|
||||
end
|
||||
|
||||
-- Jecs
|
||||
for id, pos, vel in world:query(Position, Velocity) do
|
||||
-- Process entities
|
||||
end
|
||||
```
|
||||
|
||||
## Major Feature Differences
|
||||
|
||||
### Relationships
|
||||
Jecs provides built-in support for entity relationships:
|
||||
```lua
|
||||
-- Jecs only
|
||||
world:add(child, pair(ChildOf, parent))
|
||||
```
|
||||
|
||||
### Component Traits
|
||||
Jecs allows adding traits to components:
|
||||
```lua
|
||||
-- Jecs only
|
||||
world:add(Position, Networked)
|
||||
```
|
||||
|
||||
### Query Caching
|
||||
Jecs provides explicit query caching:
|
||||
```lua
|
||||
-- Jecs only
|
||||
local cachedQuery = world:query(Position, Velocity):cached()
|
||||
```
|
||||
|
||||
## Migration Steps
|
||||
|
||||
1. **Update Component Definitions**
|
||||
- Replace Matter component tables with Jecs components
|
||||
- Add type annotations for better type safety
|
||||
|
||||
2. **Update Entity Management**
|
||||
- Replace `spawn()` with `entity()`
|
||||
- Update component insertion syntax
|
||||
|
||||
3. **Update Queries**
|
||||
- Review and update query usage
|
||||
- Consider using query caching for performance
|
||||
|
||||
4. **Add Relationships**
|
||||
- Replace custom parent-child implementations with Jecs relationships
|
||||
- Use built-in relationship features
|
||||
|
||||
5. **Update Systems**
|
||||
- Review system implementation patterns
|
||||
- Consider using component traits for better organization
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Query Performance**
|
||||
- Cache frequently used queries
|
||||
- Use appropriate filters
|
||||
|
||||
2. **Component Storage**
|
||||
- Use tags for boolean states
|
||||
- Consider component data structure size
|
||||
|
||||
3. **Relationship Usage**
|
||||
- Be mindful of relationship complexity
|
||||
- Use built-in relationships when possible
|
||||
|
||||
## Getting Help
|
||||
|
||||
Need help migrating?
|
||||
- Join our [Discord server](https://discord.gg/h2NV8PqhAD)
|
||||
- Check the [API documentation](../../api/jecs.md)
|
||||
- Open an issue on [GitHub](https://github.com/ukendio/jecs/issues)
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
|
||||
## 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
3
docs/public/robots.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
Sitemap: https://ukendio.github.io/jecs/sitemap.xml
|
16
docs/public/sitemap.xml
Normal file
16
docs/public/sitemap.xml
Normal 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>
|
Loading…
Reference in a new issue