mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Add docs (#45)
* Initial commit * Add section for standalone * Fix docs * Add pages to docs * Remove redundant files
This commit is contained in:
parent
f1ba9c4a55
commit
0567856a59
7 changed files with 665 additions and 412 deletions
|
@ -31,7 +31,7 @@ local Name = world:component()
|
|||
local function parent(entity)
|
||||
return world:target(entity, ChildOf)
|
||||
end
|
||||
local function name(entity)
|
||||
local function getName(entity)
|
||||
return world:get(entity, Name)
|
||||
end
|
||||
|
||||
|
|
45
docs/api-types.md
Normal file
45
docs/api-types.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# World
|
||||
|
||||
A World contains all ECS data
|
||||
Games can have multiple worlds, although typically only one is necessary. These worlds are isolated from each other, meaning they donot share the same entities nor component IDs.
|
||||
|
||||
---
|
||||
|
||||
# Entity
|
||||
|
||||
An unique id.
|
||||
|
||||
Entities consist out of a number unique to the entity in the lower 32 bits, and a counter used to track entity liveliness in the upper 32 bits. When an id is recycled, its generation count is increased. This causes recycled ids to be very large (>4 billion), which is normal.
|
||||
|
||||
---
|
||||
|
||||
# QueryIter
|
||||
|
||||
A result from the `World:query` function.
|
||||
|
||||
Queries are used to iterate over entities that match against the set collection of components.
|
||||
|
||||
Calling it in a loop will allow iteration over the results.
|
||||
|
||||
```lua
|
||||
for id, enemy, charge, model in world:query(Enemy, Charge, Model) do
|
||||
-- Do something
|
||||
end
|
||||
```
|
||||
|
||||
### QueryIter.without
|
||||
|
||||
QueryIter.without(iter: QueryIter
|
||||
...: [Entity](../api-types/Entity)): QueryIter
|
||||
|
||||
|
||||
Create a new Query Iterator from the filter
|
||||
|
||||
#### Parameters
|
||||
world The world.
|
||||
... The collection of components to filter archetypes against.
|
||||
|
||||
#### Returns
|
||||
|
||||
The new query iterator.
|
||||
|
19
docs/tutorials/quick-start/getting-started.md
Normal file
19
docs/tutorials/quick-start/getting-started.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Getting Started
|
||||
This section will provide a walk through setting up your development environment and a quick overview of the different features and concepts in Jecs with short examples.
|
||||
|
||||
## Installing Jecs
|
||||
|
||||
To use Jecs, you will need to add the library to your project's source folder.
|
||||
|
||||
## Installing as standalone
|
||||
Head over to the [Releases](https://github.com/ukendio/jecs/releases/latest) page and install the rbxm file.
|
||||

|
||||
|
||||
## Installing with Wally
|
||||
Jecs is available as a package on [wally.run](https://wally.run/package/ukendio/jecs)
|
||||
|
||||
Add it to your project's Wally.toml like this:
|
||||
```toml
|
||||
[dependencies]
|
||||
jecs = "0.1.0" # Make sure this is the latest version
|
||||
```
|
BIN
docs/tutorials/quick-start/rbxm.png
Normal file
BIN
docs/tutorials/quick-start/rbxm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
149
lib/init.lua
149
lib/init.lua
|
@ -6,10 +6,10 @@
|
|||
type i53 = number
|
||||
type i24 = number
|
||||
|
||||
type Ty = {i53}
|
||||
type Ty = { i53 }
|
||||
type ArchetypeId = number
|
||||
|
||||
type Column = {any}
|
||||
type Column = { any }
|
||||
|
||||
type Archetype = {
|
||||
id: number,
|
||||
|
@ -21,20 +21,19 @@ type Archetype = {
|
|||
},
|
||||
types: Ty,
|
||||
type: string | number,
|
||||
entities: {number},
|
||||
columns: {Column},
|
||||
entities: { number },
|
||||
columns: { Column },
|
||||
records: {},
|
||||
}
|
||||
|
||||
|
||||
type Record = {
|
||||
archetype: Archetype,
|
||||
row: number,
|
||||
dense: i24,
|
||||
componentRecord: ArchetypeMap
|
||||
componentRecord: ArchetypeMap,
|
||||
}
|
||||
|
||||
type EntityIndex = {dense: {[i24]: i53}, sparse: {[i53]: Record}}
|
||||
type EntityIndex = { dense: { [i24]: i53 }, sparse: { [i53]: Record } }
|
||||
|
||||
type ArchetypeRecord = number
|
||||
--[[
|
||||
|
@ -48,16 +47,16 @@ TODO:
|
|||
]]
|
||||
|
||||
type ArchetypeMap = {
|
||||
cache: {[number]: ArchetypeRecord},
|
||||
cache: { [number]: ArchetypeRecord },
|
||||
first: ArchetypeMap,
|
||||
second: ArchetypeMap,
|
||||
parent: ArchetypeMap,
|
||||
size: number
|
||||
size: number,
|
||||
}
|
||||
|
||||
type ComponentIndex = {[i24]: ArchetypeMap}
|
||||
type ComponentIndex = { [i24]: ArchetypeMap }
|
||||
|
||||
type Archetypes = {[ArchetypeId]: Archetype}
|
||||
type Archetypes = { [ArchetypeId]: Archetype }
|
||||
|
||||
type ArchetypeDiff = {
|
||||
added: Ty,
|
||||
|
@ -96,46 +95,41 @@ local function addFlags(isPair: boolean)
|
|||
end
|
||||
|
||||
local function ECS_COMBINE(source: number, target: number): i53
|
||||
local e = source * 2^28 + target * ECS_ID_FLAGS_MASK
|
||||
local e = source * 268435456 + target * ECS_ID_FLAGS_MASK
|
||||
return e
|
||||
end
|
||||
|
||||
local function ECS_IS_PAIR(e: number)
|
||||
return (e % 2^4) // FLAGS_PAIR ~= 0
|
||||
end
|
||||
|
||||
function separate(entity: number)
|
||||
local _typeFlags = entity % 0x10
|
||||
entity //= ECS_ID_FLAGS_MASK
|
||||
return entity // ECS_ENTITY_MASK, entity % ECS_GENERATION_MASK, _typeFlags
|
||||
return (e % 2 ^ 4) // FLAGS_PAIR ~= 0
|
||||
end
|
||||
|
||||
-- HIGH 24 bits LOW 24 bits
|
||||
local function ECS_GENERATION(e: i53)
|
||||
e //= 0x10
|
||||
e = e // 0x10
|
||||
return e % ECS_GENERATION_MASK
|
||||
end
|
||||
|
||||
-- SECOND
|
||||
local function ECS_ENTITY_T_LO(e: i53)
|
||||
e //= 0x10
|
||||
return e // ECS_ENTITY_MASK
|
||||
end
|
||||
|
||||
local function ECS_GENERATION_INC(e: i53)
|
||||
local id, generation, flags = separate(e)
|
||||
local flags = e // 0x10
|
||||
local id = flags // ECS_ENTITY_MASK
|
||||
local generation = flags % ECS_GENERATION_MASK
|
||||
|
||||
return ECS_COMBINE(id, generation + 1) + flags
|
||||
end
|
||||
|
||||
-- FIRST gets the high ID
|
||||
local function ECS_ENTITY_T_HI(entity: i53): i24
|
||||
entity //= 0x10
|
||||
local first = entity % ECS_ENTITY_MASK
|
||||
return first
|
||||
local function ECS_ENTITY_T_HI(e: i53): i24
|
||||
e = e // 0x10
|
||||
return e % ECS_ENTITY_MASK
|
||||
end
|
||||
|
||||
local function ECS_PAIR(pred: number, obj: number)
|
||||
-- SECOND
|
||||
local function ECS_ENTITY_T_LO(e: i53)
|
||||
e = e // 0x10
|
||||
return e // ECS_ENTITY_MASK
|
||||
end
|
||||
|
||||
local function ECS_PAIR(pred: i53, obj: i53): i53
|
||||
local first
|
||||
local second: number = WILDCARD
|
||||
|
||||
|
@ -148,35 +142,28 @@ local function ECS_PAIR(pred: number, obj: number)
|
|||
second = ECS_ENTITY_T_LO(pred)
|
||||
end
|
||||
|
||||
return ECS_COMBINE(
|
||||
ECS_ENTITY_T_LO(first), second) + addFlags(--[[isPair]] true)
|
||||
return ECS_COMBINE(ECS_ENTITY_T_LO(first), second) + addFlags(--[[isPair]] true)
|
||||
end
|
||||
|
||||
local function getAlive(entityIndex: EntityIndex, id: i24)
|
||||
local entityId = entityIndex.dense[id]
|
||||
local record = entityIndex.sparse[entityIndex.dense[id]]
|
||||
if not record then
|
||||
error(id.." is not alive")
|
||||
end
|
||||
return entityId
|
||||
end
|
||||
|
||||
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
|
||||
local function ECS_PAIR_RELATION(entityIndex, e)
|
||||
assert(ECS_IS_PAIR(e))
|
||||
return getAlive(entityIndex, ECS_ENTITY_T_HI(e))
|
||||
end
|
||||
|
||||
-- ECS_PAIR_SECOND gets the relationship / pred / LOW bits
|
||||
local function ECS_PAIR_OBJECT(entityIndex, e)
|
||||
assert(ECS_IS_PAIR(e))
|
||||
return getAlive(entityIndex, ECS_ENTITY_T_LO(e))
|
||||
end
|
||||
|
||||
local function nextEntityId(entityIndex, index: i24): i53
|
||||
local id = ECS_COMBINE(index, 0)
|
||||
entityIndex.sparse[id] = {
|
||||
dense = index
|
||||
dense = index,
|
||||
} :: Record
|
||||
entityIndex.dense[index] = id
|
||||
|
||||
|
@ -252,7 +239,7 @@ local function newEntity(entityId: i53, record: Record, archetype: Archetype)
|
|||
return record
|
||||
end
|
||||
|
||||
local function moveEntity(entityIndex, entityId: i53, record: Record, to: Archetype)
|
||||
local function moveEntity(entityIndex: EntityIndex, entityId: i53, record: Record, to: Archetype)
|
||||
local sourceRow = record.row
|
||||
local from = record.archetype
|
||||
local destinationRow = archetypeAppend(entityId, to)
|
||||
|
@ -265,11 +252,16 @@ local function hash(arr): string | number
|
|||
return table.concat(arr, "_")
|
||||
end
|
||||
|
||||
local function ensureComponentRecord(componentIndex: ComponentIndex, archetypeId, componentId, i): ArchetypeMap
|
||||
local function ensureComponentRecord(
|
||||
componentIndex: ComponentIndex,
|
||||
archetypeId: number,
|
||||
componentId: number,
|
||||
i: number
|
||||
): ArchetypeMap
|
||||
local archetypesMap = componentIndex[componentId]
|
||||
|
||||
if not archetypesMap then
|
||||
archetypesMap = {size = 0, cache = {}, first = {}, second = {}} :: ArchetypeMap
|
||||
archetypesMap = { size = 0, cache = {}, first = {}, second = {} } :: ArchetypeMap
|
||||
componentIndex[componentId] = archetypesMap
|
||||
end
|
||||
|
||||
|
@ -286,8 +278,7 @@ local function ECS_ID_IS_WILDCARD(e)
|
|||
return first == WILDCARD or second == WILDCARD
|
||||
end
|
||||
|
||||
|
||||
local function archetypeOf(world: any, types: {i24}, prev: Archetype?): Archetype
|
||||
local function archetypeOf(world: any, types: { i24 }, prev: Archetype?): Archetype
|
||||
local ty = hash(types)
|
||||
|
||||
local id = world.nextArchetypeId + 1
|
||||
|
@ -306,26 +297,24 @@ local function archetypeOf(world: any, types: {i24}, prev: Archetype?): Archetyp
|
|||
local object = ECS_PAIR_OBJECT(world.entityIndex, componentId)
|
||||
|
||||
local idr_r = ECS_PAIR(relation, WILDCARD)
|
||||
ensureComponentRecord(
|
||||
componentIndex, id, idr_r, i)
|
||||
ensureComponentRecord(componentIndex, id, idr_r, i)
|
||||
records[idr_r] = i
|
||||
|
||||
local idr_t = ECS_PAIR(WILDCARD, object)
|
||||
ensureComponentRecord(
|
||||
componentIndex, id, idr_t, i)
|
||||
ensureComponentRecord(componentIndex, id, idr_t, i)
|
||||
records[idr_t] = i
|
||||
end
|
||||
columns[i] = {}
|
||||
end
|
||||
|
||||
local archetype = {
|
||||
columns = columns;
|
||||
edges = {};
|
||||
entities = {};
|
||||
id = id;
|
||||
records = records;
|
||||
type = ty;
|
||||
types = types;
|
||||
columns = columns,
|
||||
edges = {},
|
||||
entities = {},
|
||||
id = id,
|
||||
records = records,
|
||||
type = ty,
|
||||
types = types,
|
||||
}
|
||||
world.archetypeIndex[ty] = archetype
|
||||
world.archetypes[id] = archetype
|
||||
|
@ -337,20 +326,20 @@ local World = {}
|
|||
World.__index = World
|
||||
function World.new()
|
||||
local self = setmetatable({
|
||||
archetypeIndex = {};
|
||||
archetypes = {} :: Archetypes;
|
||||
componentIndex = {} :: ComponentIndex;
|
||||
archetypeIndex = {},
|
||||
archetypes = {} :: Archetypes,
|
||||
componentIndex = {} :: ComponentIndex,
|
||||
entityIndex = {
|
||||
dense = {},
|
||||
sparse = {}
|
||||
} :: EntityIndex;
|
||||
sparse = {},
|
||||
} :: EntityIndex,
|
||||
hooks = {
|
||||
[ON_ADD] = {};
|
||||
};
|
||||
nextArchetypeId = 0;
|
||||
nextComponentId = 0;
|
||||
nextEntityId = 0;
|
||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype;
|
||||
[ON_ADD] = {},
|
||||
},
|
||||
nextArchetypeId = 0,
|
||||
nextComponentId = 0,
|
||||
nextEntityId = 0,
|
||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||
}, World)
|
||||
self.ROOT_ARCHETYPE = archetypeOf(self, {})
|
||||
return self
|
||||
|
@ -477,7 +466,7 @@ local function ensureArchetype(world: World, types, prev)
|
|||
return archetypeOf(world, types, prev)
|
||||
end
|
||||
|
||||
local function findInsert(types: {i53}, toAdd: i53)
|
||||
local function findInsert(types: { i53 }, toAdd: i53)
|
||||
for i, id in types do
|
||||
if id == toAdd then
|
||||
return -1
|
||||
|
@ -644,20 +633,20 @@ end
|
|||
|
||||
-- the less creation the better
|
||||
local function actualNoOperation() end
|
||||
local function noop(_self: Query, ...: i53): () -> (number, ...any)
|
||||
local function noop(_self: Query, ...): () -> ()
|
||||
return actualNoOperation :: any
|
||||
end
|
||||
|
||||
local EmptyQuery = {
|
||||
__iter = noop;
|
||||
without = noop;
|
||||
__iter = noop,
|
||||
without = noop,
|
||||
}
|
||||
EmptyQuery.__index = EmptyQuery
|
||||
setmetatable(EmptyQuery, EmptyQuery)
|
||||
|
||||
export type Query = typeof(EmptyQuery)
|
||||
|
||||
function World.query(world: World, ...: i53): Query
|
||||
function World.query(world: World, ...): Query
|
||||
-- breaking?
|
||||
if (...) == nil then
|
||||
error("Missing components")
|
||||
|
@ -666,7 +655,7 @@ function World.query(world: World, ...: i53): Query
|
|||
local compatibleArchetypes = {}
|
||||
local length = 0
|
||||
|
||||
local components = {...}
|
||||
local components = { ... }
|
||||
local archetypes = world.archetypes
|
||||
local queryLength = #components
|
||||
|
||||
|
@ -708,7 +697,7 @@ function World.query(world: World, ...: i53): Query
|
|||
length += 1
|
||||
compatibleArchetypes[length] = {
|
||||
archetype = archetype,
|
||||
indices = indices
|
||||
indices = indices,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -721,7 +710,7 @@ function World.query(world: World, ...: i53): Query
|
|||
preparedQuery.__index = preparedQuery
|
||||
|
||||
function preparedQuery:without(...)
|
||||
local withoutComponents = {...}
|
||||
local withoutComponents = { ... }
|
||||
for i = #compatibleArchetypes, 1, -1 do
|
||||
local archetype = compatibleArchetypes[i].archetype
|
||||
local records = archetype.records
|
||||
|
@ -857,11 +846,11 @@ function World.__iter(world: World): () -> (number?, unknown?)
|
|||
end
|
||||
|
||||
return table.freeze({
|
||||
World = World;
|
||||
World = World,
|
||||
|
||||
OnAdd = ON_ADD;
|
||||
OnRemove = ON_REMOVE;
|
||||
OnSet = ON_SET;
|
||||
OnAdd = ON_ADD,
|
||||
OnRemove = ON_REMOVE,
|
||||
OnSet = ON_SET,
|
||||
Wildcard = WILDCARD,
|
||||
w = WILDCARD,
|
||||
Rest = REST,
|
||||
|
|
186
mkdocs.yml
Normal file
186
mkdocs.yml
Normal file
|
@ -0,0 +1,186 @@
|
|||
site_name: Fusion
|
||||
site_url: https://elttob.uk/Fusion/
|
||||
repo_name: dphfox/Fusion
|
||||
repo_url: https://github.com/dphfox/Fusion
|
||||
|
||||
extra:
|
||||
version:
|
||||
provider: mike
|
||||
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/assets/overrides
|
||||
logo: assets/logo
|
||||
favicon: assets/logo-dark.svg
|
||||
palette:
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: fusiondoc-dark
|
||||
toggle:
|
||||
icon: octicons/sun-24
|
||||
title: Switch to light theme
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: fusiondoc-light
|
||||
toggle:
|
||||
icon: octicons/moon-24
|
||||
title: Switch to dark theme
|
||||
font:
|
||||
text: Plus Jakarta Sans
|
||||
code: JetBrains Mono
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.top
|
||||
- navigation.sections
|
||||
- navigation.instant
|
||||
- navigation.indexes
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
icon:
|
||||
repo: octicons/mark-github-16
|
||||
|
||||
extra_css:
|
||||
- assets/theme/fusiondoc.css
|
||||
- assets/theme/colours.css
|
||||
- assets/theme/code.css
|
||||
- assets/theme/paragraph.css
|
||||
- assets/theme/page.css
|
||||
- assets/theme/admonition.css
|
||||
- assets/theme/404.css
|
||||
- assets/theme/api-reference.css
|
||||
- assets/theme/dev-tools.css
|
||||
|
||||
extra_javascript:
|
||||
- assets/scripts/smooth-scroll.js
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Tutorials:
|
||||
- Get Started: tutorials/index.md
|
||||
- Installing Fusion: tutorials/get-started/installing-fusion.md
|
||||
- Developer Tools: tutorials/get-started/developer-tools.md
|
||||
- Getting Help: tutorials/get-started/getting-help.md
|
||||
- Fundamentals:
|
||||
- Scopes: tutorials/fundamentals/scopes.md
|
||||
- Values: tutorials/fundamentals/values.md
|
||||
- Observers: tutorials/fundamentals/observers.md
|
||||
- Computeds: tutorials/fundamentals/computeds.md
|
||||
- Tables:
|
||||
- ForValues: tutorials/tables/forvalues.md
|
||||
- ForKeys: tutorials/tables/forkeys.md
|
||||
- ForPairs: tutorials/tables/forpairs.md
|
||||
- Animation:
|
||||
- Tweens: tutorials/animation/tweens.md
|
||||
- Springs: tutorials/animation/springs.md
|
||||
- Roblox:
|
||||
- Hydration: tutorials/roblox/hydration.md
|
||||
- New Instances: tutorials/roblox/new-instances.md
|
||||
- Parenting: tutorials/roblox/parenting.md
|
||||
- Events: tutorials/roblox/events.md
|
||||
- Change Events: tutorials/roblox/change-events.md
|
||||
- Outputs: tutorials/roblox/outputs.md
|
||||
- References: tutorials/roblox/references.md
|
||||
- Best Practices:
|
||||
- Components: tutorials/best-practices/components.md
|
||||
- Instance Handling: tutorials/best-practices/instance-handling.md
|
||||
- Callbacks: tutorials/best-practices/callbacks.md
|
||||
- State: tutorials/best-practices/state.md
|
||||
- Sharing Values: tutorials/best-practices/sharing-values.md
|
||||
- Error Safety: tutorials/best-practices/error-safety.md
|
||||
- Optimisation: tutorials/best-practices/optimisation.md
|
||||
|
||||
- Examples:
|
||||
- Home: examples/index.md
|
||||
- Cookbook:
|
||||
- examples/cookbook/index.md
|
||||
- Player List: examples/cookbook/player-list.md
|
||||
- Animated Computed: examples/cookbook/animated-computed.md
|
||||
- Fetch Data From Server: examples/cookbook/fetch-data-from-server.md
|
||||
- Light & Dark Theme: examples/cookbook/light-and-dark-theme.md
|
||||
- Button Component: examples/cookbook/button-component.md
|
||||
- Loading Spinner: examples/cookbook/loading-spinner.md
|
||||
- Drag & Drop: examples/cookbook/drag-and-drop.md
|
||||
- API Reference:
|
||||
- api-reference/index.md
|
||||
- General:
|
||||
- Errors: api-reference/general/errors.md
|
||||
- Types:
|
||||
- Contextual: api-reference/general/types/contextual.md
|
||||
- Version: api-reference/general/types/version.md
|
||||
- Members:
|
||||
- Contextual: api-reference/general/members/contextual.md
|
||||
- Safe: api-reference/general/members/safe.md
|
||||
- version: api-reference/general/members/version.md
|
||||
- Memory:
|
||||
- Types:
|
||||
- Scope: api-reference/memory/types/scope.md
|
||||
- ScopedObject: api-reference/memory/types/scopedobject.md
|
||||
- Task: api-reference/memory/types/task.md
|
||||
- Members:
|
||||
- deriveScope: api-reference/memory/members/derivescope.md
|
||||
- doCleanup: api-reference/memory/members/docleanup.md
|
||||
- scoped: api-reference/memory/members/scoped.md
|
||||
- State:
|
||||
- Types:
|
||||
- UsedAs: api-reference/state/types/usedas.md
|
||||
- Computed: api-reference/state/types/computed.md
|
||||
- Dependency: api-reference/state/types/dependency.md
|
||||
- Dependent: api-reference/state/types/dependent.md
|
||||
- For: api-reference/state/types/for.md
|
||||
- Observer: api-reference/state/types/observer.md
|
||||
- StateObject: api-reference/state/types/stateobject.md
|
||||
- Use: api-reference/state/types/use.md
|
||||
- Value: api-reference/state/types/value.md
|
||||
- Members:
|
||||
- Computed: api-reference/state/members/computed.md
|
||||
- ForKeys: api-reference/state/members/forkeys.md
|
||||
- ForPairs: api-reference/state/members/forpairs.md
|
||||
- ForValues: api-reference/state/members/forvalues.md
|
||||
- Observer: api-reference/state/members/observer.md
|
||||
- peek: api-reference/state/members/peek.md
|
||||
- Value: api-reference/state/members/value.md
|
||||
- Roblox:
|
||||
- Types:
|
||||
- Child: api-reference/roblox/types/child.md
|
||||
- PropertyTable: api-reference/roblox/types/propertytable.md
|
||||
- SpecialKey: api-reference/roblox/types/specialkey.md
|
||||
- Members:
|
||||
- Attribute: api-reference/roblox/members/attribute.md
|
||||
- AttributeChange: api-reference/roblox/members/attributechange.md
|
||||
- AttributeOut: api-reference/roblox/members/attributeout.md
|
||||
- Children: api-reference/roblox/members/children.md
|
||||
- Hydrate: api-reference/roblox/members/hydrate.md
|
||||
- New: api-reference/roblox/members/new.md
|
||||
- OnChange: api-reference/roblox/members/onchange.md
|
||||
- OnEvent: api-reference/roblox/members/onevent.md
|
||||
- Out: api-reference/roblox/members/out.md
|
||||
- Ref: api-reference/roblox/members/ref.md
|
||||
- Animation:
|
||||
- Types:
|
||||
- Animatable: api-reference/animation/types/animatable.md
|
||||
- Spring: api-reference/animation/types/spring.md
|
||||
- Tween: api-reference/animation/types/tween.md
|
||||
- Members:
|
||||
- Tween: api-reference/animation/members/tween.md
|
||||
- Spring: api-reference/animation/members/spring.md
|
||||
- Extras:
|
||||
- Home: extras/index.md
|
||||
- Backgrounds: extras/backgrounds.md
|
||||
- Brand Guidelines: extras/brand-guidelines.md
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- attr_list
|
||||
- meta
|
||||
- md_in_html
|
||||
- pymdownx.superfences
|
||||
- pymdownx.betterem
|
||||
- pymdownx.details
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.inlinehilite
|
||||
- toc:
|
||||
permalink: true
|
||||
- pymdownx.highlight:
|
||||
guess_lang: false
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
|
@ -1,5 +1,5 @@
|
|||
local testkit = require("../testkit")
|
||||
local jecs = require("../lib/init")
|
||||
local testkit = require("../testkit")
|
||||
local __ = jecs.Wildcard
|
||||
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
|
||||
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
|
||||
|
@ -16,14 +16,15 @@ local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
|||
if not CHECK(not ok, 2) then
|
||||
local i = string.find(err :: string, " ")
|
||||
assert(i)
|
||||
local msg = string.sub(err :: string, i+1)
|
||||
local msg = string.sub(err :: string, i + 1)
|
||||
CHECK(msg == s, 2)
|
||||
end
|
||||
end
|
||||
local N = 10
|
||||
|
||||
TEST("world", function()
|
||||
do CASE "should be iterable"
|
||||
do
|
||||
CASE("should be iterable")
|
||||
local world = jecs.World.new()
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
@ -55,7 +56,8 @@ TEST("world", function()
|
|||
CHECK(count == 3 + 2)
|
||||
end
|
||||
|
||||
do CASE "should query all matching entities"
|
||||
do
|
||||
CASE("should query all matching entities")
|
||||
local world = jecs.World.new()
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
@ -65,7 +67,9 @@ TEST("world", function()
|
|||
local id = world:entity()
|
||||
|
||||
world:set(id, A, true)
|
||||
if i > 5 then world:set(id, B, true) end
|
||||
if i > 5 then
|
||||
world:set(id, B, true)
|
||||
end
|
||||
entities[i] = id
|
||||
end
|
||||
|
||||
|
@ -74,10 +78,10 @@ TEST("world", function()
|
|||
end
|
||||
|
||||
CHECK(#entities == 0)
|
||||
|
||||
end
|
||||
|
||||
do CASE "should query all matching entities when irrelevant component is removed"
|
||||
do
|
||||
CASE("should query all matching entities when irrelevant component is removed")
|
||||
local world = jecs.World.new()
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
@ -91,7 +95,9 @@ TEST("world", function()
|
|||
-- https://github.com/Ukendio/jecs/pull/15
|
||||
world:set(id, B, true)
|
||||
world:set(id, A, true)
|
||||
if i > 5 then world:remove(id, B) end
|
||||
if i > 5 then
|
||||
world:remove(id, B)
|
||||
end
|
||||
entities[i] = id
|
||||
end
|
||||
|
||||
|
@ -104,7 +110,8 @@ TEST("world", function()
|
|||
CHECK(added == N)
|
||||
end
|
||||
|
||||
do CASE "should query all entities without B"
|
||||
do
|
||||
CASE("should query all entities without B")
|
||||
local world = jecs.World.new()
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
@ -119,7 +126,6 @@ TEST("world", function()
|
|||
else
|
||||
world:set(id, B, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
for id in world:query(A):without(B) do
|
||||
|
@ -127,10 +133,10 @@ TEST("world", function()
|
|||
end
|
||||
|
||||
CHECK(#entities == 0)
|
||||
|
||||
end
|
||||
|
||||
do CASE "should allow setting components in arbitrary order"
|
||||
do
|
||||
CASE("should allow setting components in arbitrary order")
|
||||
local world = jecs.World.new()
|
||||
|
||||
local Health = world:entity()
|
||||
|
@ -143,7 +149,8 @@ TEST("world", function()
|
|||
CHECK(world:get(id, Poison) == 5)
|
||||
end
|
||||
|
||||
do CASE "should allow deleting components"
|
||||
do
|
||||
CASE("should allow deleting components")
|
||||
local world = jecs.World.new()
|
||||
|
||||
local Health = world:entity()
|
||||
|
@ -162,10 +169,10 @@ TEST("world", function()
|
|||
CHECK(world:get(id, Health) == nil)
|
||||
CHECK(world:get(id1, Poison) == 500)
|
||||
CHECK(world:get(id1, Health) == 50)
|
||||
|
||||
end
|
||||
|
||||
do CASE "should allow remove that doesn't exist on entity"
|
||||
do
|
||||
CASE("should allow remove that doesn't exist on entity")
|
||||
local world = jecs.World.new()
|
||||
|
||||
local Health = world:entity()
|
||||
|
@ -179,7 +186,8 @@ TEST("world", function()
|
|||
CHECK(world:get(id, Health) == 50)
|
||||
end
|
||||
|
||||
do CASE "should increment generation"
|
||||
do
|
||||
CASE("should increment generation")
|
||||
local world = jecs.World.new()
|
||||
local e = world:entity()
|
||||
CHECK(ECS_ID(e) == 1 + jecs.Rest)
|
||||
|
@ -189,7 +197,8 @@ TEST("world", function()
|
|||
CHECK(ECS_GENERATION(e) == 1) -- 1
|
||||
end
|
||||
|
||||
do CASE "should get alive from index in the dense array"
|
||||
do
|
||||
CASE("should get alive from index in the dense array")
|
||||
local world = jecs.World.new()
|
||||
local _e = world:entity()
|
||||
local e2 = world:entity()
|
||||
|
@ -203,7 +212,8 @@ TEST("world", function()
|
|||
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
|
||||
end
|
||||
|
||||
do CASE "should allow querying for relations"
|
||||
do
|
||||
CASE("should allow querying for relations")
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
local Apples = world:entity()
|
||||
|
@ -216,7 +226,8 @@ TEST("world", function()
|
|||
end
|
||||
end
|
||||
|
||||
do CASE "should allow wildcards in queries"
|
||||
do
|
||||
CASE("should allow wildcards in queries")
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
local Apples = world:entity()
|
||||
|
@ -235,11 +246,12 @@ TEST("world", function()
|
|||
end
|
||||
end
|
||||
|
||||
do CASE "should match against multiple pairs"
|
||||
do
|
||||
CASE("should match against multiple pairs")
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
local Apples = world:entity()
|
||||
local Oranges =world:entity()
|
||||
local Oranges = world:entity()
|
||||
local bob = world:entity()
|
||||
local alice = world:entity()
|
||||
|
||||
|
@ -267,7 +279,8 @@ TEST("world", function()
|
|||
CHECK(count == 1)
|
||||
end
|
||||
|
||||
do CASE "should only relate alive entities"
|
||||
do
|
||||
CASE("should only relate alive entities")
|
||||
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
|
@ -294,7 +307,8 @@ TEST("world", function()
|
|||
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
|
||||
end
|
||||
|
||||
do CASE "should error when setting invalid pair"
|
||||
do
|
||||
CASE("should error when setting invalid pair")
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
local Apples = world:entity()
|
||||
|
@ -302,12 +316,11 @@ TEST("world", function()
|
|||
|
||||
world:delete(Apples)
|
||||
|
||||
CHECK_NO_ERR("Apples should be dead", function()
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||
end)
|
||||
end
|
||||
|
||||
do CASE "should find target for ChildOf"
|
||||
do
|
||||
CASE("should find target for ChildOf")
|
||||
local world = jecs.World.new()
|
||||
|
||||
local ChildOf = world:component()
|
||||
|
@ -337,3 +350,4 @@ TEST("world", function()
|
|||
end)
|
||||
|
||||
FINISH()
|
||||
|
||||
|
|
Loading…
Reference in a new issue