Properly link flecs library

This commit is contained in:
2023-11-09 11:38:29 +01:00
parent dc585396c3
commit 8edcf9305c
1392 changed files with 390081 additions and 164 deletions

View File

@@ -7,17 +7,16 @@ add_compile_definitions(DEBUG_MODE)
set(BUILD_EXAMPLES false)
add_subdirectory(libs/raylib-4.5.0)
add_subdirectory(libs/flecs)
set(librarySources
libs/cute_tiled/cute_tiled.c
libs/flecs/flecs.c
libs/nuklear/nuklear.c
libs/raygui/raygui.c
)
set(libraryDirs
libs/cute_tiled
libs/flecs
libs/nuklear
libs/raygui
)
@@ -67,7 +66,7 @@ add_library(Breeze STATIC
set_target_properties(Breeze PROPERTIES
PUBLIC_HEADER "${BreezeHeaders}")
target_link_libraries(Breeze raylib)
target_link_libraries(Breeze raylib flecs::flecs_static)
#target_include_directories(Breeze PUBLIC .)
target_include_directories(Breeze

View File

@@ -71,6 +71,7 @@ static void createColliders(BzTileMap *map) {
// Top-most layer takes priority
for (i32 i = map->layerCount - 1; i >= 0; i--) {
BzTileLayer *layer = map->layers + i;
if (!layer->data) continue;
if (layer->tilesetIdx == -1) continue;
BzTileset *tileset = map->tilesets + layer->tilesetIdx;
for (i32 y = 0; y < map->height; y++) {

View File

@@ -0,0 +1 @@
examples/build/bazel

View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = false

12
engine/libs/flecs/.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [SanderMertens]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -0,0 +1,24 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here (operating system, hardware, ...).

View File

@@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Describe the problem you are trying to solve.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]. Refrain from proposing a solution here.
**Describe the solution you'd like**
A clear and concise description of what you would like to happen.

View File

@@ -0,0 +1,833 @@
name: CI
on: [ push, pull_request ]
jobs:
build-linux:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: |
bake --strict
build-macos:
runs-on: macOS-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: |
bake --strict
build-windows:
runs-on: windows-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: ilammy/msvc-dev-cmd@v1
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
cd bake\build-Windows
nmake
cd ..
./bake setup --local
- name: build flecs (debug)
run: bake/bake --strict --cfg debug
- name: build flecs (release)
run: bake/bake --strict --cfg release
- name: build examples (debug)
run: bake/bake examples --strict --cfg debug
- name: build examples (release)
run: bake/bake examples --strict --cfg debug
build-msys:
runs-on: windows-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- { sys: mingw64 }
- { sys: mingw32 }
- { sys: ucrt64 }
- { sys: clang64 }
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.sys}}
update: true
install: >-
curl
git
pacboy: >-
toolchain:p
cmake:p
- name: create cmake build folders
run: |
mkdir cmake_build
mkdir examples/c/cmake_build
mkdir examples/cpp/cmake_build
- name: build flecs
working-directory: cmake_build
run: |
cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c examples
working-directory: examples/c/cmake_build
run: |
cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c++ examples
working-directory: examples/cpp/cmake_build
run: |
cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON ..
cmake --build . -j 4
build:
needs: build-linux
runs-on: ${{ matrix.target.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
target:
- { os: ubuntu-20.04, cc: gcc-7, cxx: g++-7 }
- { os: ubuntu-20.04, cc: gcc-8, cxx: g++-8 }
- { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 }
- { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 }
- { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 }
- { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 }
- { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 }
- { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 }
- { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 }
- { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 }
- { os: ubuntu-20.04, cc: clang-11, cxx: clang++-11 }
- { os: ubuntu-20.04, cc: clang-12, cxx: clang++-12 }
- { os: ubuntu-latest, cc: clang-13, cxx: clang++-13 }
- { os: ubuntu-latest, cc: clang-14, cxx: clang++-14 }
- { os: ubuntu-latest, cc: clang-15, cxx: clang++-15 }
env:
CC: ${{ matrix.target.cc }}
CXX: ${{ matrix.target.cxx }}
steps:
- uses: actions/checkout@v3
- name: install compiler
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.target.cc }}
sudo apt-get install -y ${{ matrix.target.cxx }}
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs (debug)
run: |
bake --strict --cfg debug
- name: build flecs (release)
run: |
bake --strict --cfg release
- name: build examples (debug)
run: |
bake examples/c --strict --cfg debug
bake examples/cpp --strict --cfg debug
bake examples/os_api --strict --cfg debug
- name: build examples (release)
run: |
bake examples/c --strict --cfg release
bake examples/cpp --strict --cfg release
bake examples/os_api --strict --cfg release
build-cmake:
needs: build-linux
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macOS-latest ]
steps:
- uses: actions/checkout@v3
- name: create cmake build folders
run: |
mkdir cmake_build
mkdir examples/c/cmake_build
mkdir examples/cpp/cmake_build
- name: build flecs
working-directory: cmake_build
run: |
cmake -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c examples
working-directory: examples/c/cmake_build
run: |
cmake -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c++ examples
working-directory: examples/cpp/cmake_build
run: |
cmake -DFLECS_STRICT=ON ..
cmake --build . -j 4
build-cmake-windows:
needs: build-windows
runs-on: windows-latest
timeout-minutes: 30
strategy:
matrix:
toolset: [default, v141, v142, clang-cl]
include:
- toolset: v141
toolset_option: -T"v141"
- toolset: v142
toolset_option: -T"v142"
- toolset: clang-cl
toolset_option: -T"ClangCl"
steps:
- uses: actions/checkout@v3
- name: create cmake build folders
run: |
mkdir cmake_build
mkdir examples/c/cmake_build
mkdir examples/cpp/cmake_build
- name: build flecs
working-directory: cmake_build
run: |
cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c examples
working-directory: examples/c/cmake_build
run: |
cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON ..
cmake --build . -j 4
- name: build c++ examples
working-directory: examples/cpp/cmake_build
run: |
cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON ..
cmake --build . -j 4
build-meson:
needs: build-linux
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: install meson
run: |
pip3 install meson
pip3 install ninja==1.10.2.4
- name: create build folder
run: meson meson_build
- name: build flecs
working-directory: meson_build
run: |
meson compile
build-custom:
needs: build-linux
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
compiler: [ gcc, clang ]
steps:
- uses: actions/checkout@v3
- name: Install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: FLECS_SOFT_ASSERT flag
run: |
bake rebuild --strict -D FLECS_SOFT_ASSERT
bake rebuild --strict --cfg release -D FLECS_SOFT_ASSERT
- name: FLECS_KEEP_ASSERT flag
run: |
bake rebuild --strict -D FLECS_KEEP_ASSERT
bake rebuild --strict --cfg release -D FLECS_KEEP_ASSERT
- name: no extensions
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD
- name: FLECS_SYSTEM
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SYSTEM
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SYSTEM
- name: FLECS_PIPELINE
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PIPELINE
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PIPELINE
- name: FLECS_TIMER
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_TIMER
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_TIMER
- name: FLECS_MODULE
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_MODULE
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_MODULE
- name: FLECS_SNAPSHOT
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT
- name: FLECS_STATS
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_STATS
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_STATS
- name: FLECS_PARSER
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PARSER
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PARSER
- name: FLECS_PLECS
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PLECS
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PLECS
- name: FLECS_META
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META
- name: FLECS_META_C
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META_C
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META_C
- name: FLECS_EXPR
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_EXPR
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_EXPR
- name: FLECS_JSON
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_JSON
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_JSON
- name: FLECS_DOC
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_DOC
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_DOC
- name: FLECS_COREDOC
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC
- name: FLECS_LOG
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_LOG
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_LOG
- name: FLECS_APP
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_APP
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_APP
- name: FLECS_OS_API_IMPL
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_OS_API_IMPL
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_OS_API_IMPL
- name: FLECS_HTTP
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_HTTP
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_HTTP
- name: FLECS_REST
run: |
bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_REST
bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_REST
- name: FLECS_NO_LOG
run: |
bake rebuild --strict -D FLECS_NO_LOG
bake rebuild --strict --cfg release -D FLECS_NO_LOG
- name: FLECS_DEFAULT
run: |
bake rebuild --strict
bake rebuild --strict --cfg release
- name: custom_build tests
run: |
bake rebuild test/custom_builds --strict
bake runall test/custom_builds
build-amalgamated:
needs: build-linux
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
compiler: [ gcc, clang ]
steps:
- uses: actions/checkout@v3
- name: build flecs
run: ${{ matrix.compiler }} flecs.c --shared -fPIC -pedantic -Wall -Wextra -Wno-unused-parameter -Werror -Wshadow -Wconversion -Wno-missing-field-initializers
build-scan-build:
needs: build-linux
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: install clang-build
run: |
sudo apt-get install -y clang-tools
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: run scan-build
run: |
scan-build --status-bugs bake
test-c-unix:
needs: [build-linux, build-macos]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macOS-latest ]
steps:
- uses: actions/checkout@v3
- name: compiler version
run: |
gcc --version
clang --version
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: test api
run: bake run test/api -- -j 8
- name: test addons
run: bake run test/addons -- -j 8
- name: test meta
run: bake run test/meta -- -j 8
- name: test collections
run: bake run test/collections -- -j 8
test-cpp-unix:
needs: [build-linux]
runs-on: ${{ matrix.target.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
target:
- { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 }
- { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 }
- { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 }
- { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 }
- { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 }
- { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 }
- { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 }
- { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 }
- { os: ubuntu-20.04, cc: clang-11, cxx: clang++-11 }
- { os: ubuntu-20.04, cc: clang-12, cxx: clang++-12 }
- { os: ubuntu-latest, cc: clang-13, cxx: clang++-13 }
- { os: ubuntu-latest, cc: clang-14, cxx: clang++-14 }
- { os: ubuntu-latest, cc: clang-15, cxx: clang++-15 }
env:
CC: ${{ matrix.target.cc }}
CXX: ${{ matrix.target.cxx }}
steps:
- uses: actions/checkout@v3
- name: install compiler
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.target.cc }}
sudo apt-get install -y ${{ matrix.target.cxx }}
- name: compiler version
run: |
${{ matrix.target.cc }} --version
${{ matrix.target.cxx }} --version
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: test c++
run: bake run test/cpp_api -- -j 8
test-cpp-macos:
needs: [build-macos]
runs-on: ${{ matrix.os.version }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os:
- { version: macOS-11, xcode: '11.7' }
- { version: macOS-11, xcode: '12.4' }
- { version: macOS-11, xcode: '13.0' }
- { version: macOS-12, xcode: '13.1' }
- { version: macOS-12, xcode: '14.0' }
env:
CC: clang
CXX: clang++
steps:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.os.xcode }}
- name: compiler version
run: |
clang --version
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: test c++
run: bake run test/cpp_api -- -j 8
test-windows:
needs: build-windows
runs-on: windows-latest
timeout-minutes: 30
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: ilammy/msvc-dev-cmd@v1
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
cd bake\build-Windows
nmake
cd ..
./bake setup --local
- name: build flecs
run: bake/bake
- name: test api
run: bake/bake run test\api -- -j 8
- name: test addons
run: bake/bake run test\addons -- -j 8
- name: test meta
run: bake/bake run test\meta -- -j 8
- name: test collections
run: bake/bake run test\collections -- -j 8
- name: test c++
run: bake/bake run test\cpp_api -- -j 8
test-msys:
runs-on: windows-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- { sys: mingw64 }
- { sys: mingw32 }
- { sys: ucrt64 }
- { sys: clang64 }
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.sys}}
update: true
install: >-
curl
git
pacboy: >-
toolchain:p
cmake:p
- name: install bake
run: |
cp `which mingw32-make` /usr/bin/make
git clone https://github.com/SanderMertens/bake
make -C bake/build-Mingw
bake/bake setup
- name: build flecs
run: bake/bake --strict
- name: test api
run: bake/bake run test/api -- -j 8
- name: test addons
run: bake/bake run test/addons -- -j 8
- name: test meta
run: bake/bake run test/meta -- -j 8
- name: test collections
run: bake/bake run test/collections -- -j 8
- name: test c++
run: bake/bake run test/cpp_api -- -j 8
test-sanitized-api:
needs: [ build-linux ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: run tests
run: |
bake run test/api --cfg sanitize -- -j 8
test-sanitized-addons:
needs: [ build-linux ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: run tests
run: |
bake run test/addons --cfg sanitize -- -j 8
test-sanitized-meta:
needs: [ build-linux ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: run tests
run: |
bake run test/meta --cfg sanitize -- -j 8
test-sanitized-collections:
needs: [ build-linux ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: run tests
run: |
bake run test/collections --cfg sanitize -- -j 8
test-sanitized-cpp_api:
needs: [ build-linux ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v3
- name: install bake
run: |
git clone https://github.com/SanderMertens/bake
make -C bake/build-$(uname)
bake/bake setup
- name: build flecs
run: bake --strict
- name: run tests
run: |
bake run test/cpp_api --cfg sanitize -- -j 8
test-cmake:
needs: [ build-linux, build-macos ]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macOS-latest ]
steps:
- uses: actions/checkout@v3
- name: clone bake
run: |
git clone https://github.com/SanderMertens/bake
- name: build flecs & tests
run: |
cmake -DFLECS_TESTS=ON -DBAKE_DIRECTORY=bake
cmake --build . -j 4
- name: run tests
run: |
ctest -C Debug --verbose

View File

@@ -0,0 +1,50 @@
# Simple workflow for deploying static content to GitHub Pages
name: Pages deployment
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Doxygen
uses: mattnotmitt/doxygen-action@v1.9.5
with:
doxyfile-path: 'docs/cfg/Doxyfile'
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: 'docs/html'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

18
engine/libs/flecs/.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
.DS_Store
bin
.bake_cache
.vscode
gcov
.idea
*.pdb
deps
cmake-build-debug
bazel-bin
bazel-flecs
bazel-bazel
bazel-genfiles
bazel-out
bazel-testlogs
**/CMakeFiles/*
.vs
out

9
engine/libs/flecs/BUILD Normal file
View File

@@ -0,0 +1,9 @@
cc_library(
name = "flecs",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.c", "src/**/*.h", "src/**/*.inl"]),
hdrs = glob(["include/**/*.h", "include/**/*.hpp", "include/**/*.inl"]),
includes = ["include"],
)

View File

@@ -0,0 +1,98 @@
cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0063 NEW)
project(flecs LANGUAGES C)
option(FLECS_STATIC "Build static flecs lib" ON)
option(FLECS_SHARED "Build shared flecs lib" ON)
option(FLECS_PIC "Compile static flecs lib with position independent code (PIC)" ON)
option(FLECS_TESTS "Build flecs tests" OFF)
include(cmake/target_default_compile_warnings.cmake)
include(cmake/target_default_compile_options.cmake)
# Automatically generate the same folder structure in Visual Studio as we have on disk
macro(GroupSources curdir)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
GroupSources(${curdir}/${child})
else()
string(REPLACE "/" "\\" groupname ${curdir})
source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child})
endif()
endforeach()
endmacro()
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/. ${PROJECT_SOURCE_DIR}/./*)
foreach(child ${children})
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child})
GroupSources(${child})
endif()
endforeach()
file(GLOB_RECURSE INC include/*.h include/*.hpp)
file(GLOB_RECURSE SRC src/*.c)
set(FLECS_TARGETS "")
macro(add_flecs_target TARGET CONFIG)
add_library(${TARGET} ${CONFIG} ${INC} ${SRC})
add_library(flecs::${TARGET} ALIAS ${TARGET})
target_default_compile_options_c(${TARGET})
target_default_compile_warnings_c(${TARGET})
if(WIN32)
target_link_libraries(${TARGET} wsock32 ws2_32)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(${TARGET} pthread)
endif()
if(FLECS_PIC)
set_property(TARGET ${TARGET} PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
target_include_directories(${TARGET} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
list(APPEND FLECS_TARGETS ${TARGET})
endmacro()
if(FLECS_SHARED)
add_flecs_target(flecs SHARED)
endif()
if(FLECS_STATIC)
add_flecs_target(flecs_static STATIC)
target_compile_definitions(flecs_static PUBLIC flecs_STATIC)
endif()
if(FLECS_TESTS)
enable_testing()
add_subdirectory(test)
endif()
message(STATUS "Targets: ${FLECS_TARGETS}")
# define the install steps
include(GNUInstallDirs)
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
PATTERN "*.inl")
install(TARGETS ${FLECS_TARGETS}
EXPORT flecs-export
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT flecs-export
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flecs
NAMESPACE flecs::
FILE flecs-config.cmake)

21
engine/libs/flecs/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Sander Mertens
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

211
engine/libs/flecs/README.md Normal file
View File

@@ -0,0 +1,211 @@
![flecs](docs/img/logo.png)
[![Version](https://img.shields.io/github/v/release/sandermertens/flecs?include_prereleases&style=for-the-badge)](https://github.com/SanderMertens/flecs/releases)
[![MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](https://github.com/SanderMertens/flecs/blob/master/LICENSE)
[![Documentation](https://img.shields.io/badge/docs-flecs-blue?style=for-the-badge&color=blue)](https://www.flecs.dev/flecs/md_docs_Docs.html)
[![actions](https://img.shields.io/github/actions/workflow/status/SanderMertens/flecs/ci.yml?branch=master&style=for-the-badge)](https://github.com/SanderMertens/flecs/actions?query=workflow%3ACI)
[![Discord Chat](https://img.shields.io/discord/633826290415435777.svg?style=for-the-badge&color=%235a64f6)](https://discord.gg/BEzP5Rgrrp)
Flecs is a fast and lightweight Entity Component System that lets you build games and simulations with millions of entities ([join the Discord!](https://discord.gg/BEzP5Rgrrp)). Here are some of the framework's highlights:
- Fast and [portable](#language-bindings) zero dependency [C99 API](https://www.flecs.dev/flecs/group__c.html)
- Modern type-safe [C++11 API](https://www.flecs.dev/flecs/group__cpp.html) that doesn't use STL containers
- First open source ECS with full support for [Entity Relationships](https://www.flecs.dev/flecs/md_docs_Relationships.html)!
- Fast native support for [hierarchies](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md277) and [prefabs](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md305)
- Code base that builds in less than 5 seconds
- Runs [in the browser](https://flecs.dev/city) without modifications with emscripten
- Cache friendly [archetype/SoA storage](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) that can process millions of entities every frame
- Supports entities with hundreds of components and applications with tens of thousands of archetypes
- Automatic component registration that works out of the box across shared libraries/DLLs
- Write free functions with [queries](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries/basics) or run code automatically in [systems](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/pipeline)
- Run games on multiple CPU cores with a fast lockless scheduler
- Verified on all major compilers and platforms with [CI](https://github.com/SanderMertens/flecs/actions) running more than 6000 tests
- Integrated [reflection framework](https://www.flecs.dev/flecs/group__c__addons__meta.html) with [JSON serializer](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/basics_json) and support for [runtime components](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/runtime_component)
- [Unit annotations](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/units) for components
- Powerful [query language](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules) with support for [joins](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/setting_variables) and [inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/component_inheritance)
- [Statistics addon](https://www.flecs.dev/flecs/group__c__addons__stats.html) for profiling ECS performance
- A web-based UI for monitoring & controlling your apps ([demo](https://flecs.dev/explorer), [code](https://github.com/flecs-hub/explorer)):
[![Flecs Explorer](docs/img/explorer.png)](https://flecs.dev/explorer)
To support the project, give it a star 🌟 !
## What is an Entity Component System?
ECS is a way of organizing code and data that lets you build games that are larger, more complex and are easier to extend. Something is called an ECS when it:
- Has _entities_ that uniquely identify objects in a game
- Has _components_ which are datatypes that can be added to entities
- Has _systems_ which are functions that run for all entities matching a component _query_
For more information, check the [ECS FAQ](https://github.com/SanderMertens/ecs-faq)!
## Try it out!
The [Flecs playground](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js) lets you try Flecs without writing any C/C++ code!
[![Flecs playground](docs/img/playground.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js)
To learn how to use the playground, check the [Flecs Script Tutorial](https://www.flecs.dev/flecs/md_docs_FlecsScriptTutorial.html).
## Documentation
- [Quickstart](https://www.flecs.dev/flecs/md_docs_Quickstart.html)
- [FAQ](https://www.flecs.dev/flecs/md_docs_FAQ.html)
- [Examples](https://github.com/SanderMertens/flecs/tree/master/examples)
- [All Documentation](https://www.flecs.dev/flecs/md_docs_Docs.html)
## Performance
For a list of regularly tracked benchmarks, see the [ECS Benchmark](https://github.com/SanderMertens/ecs_benchmark) project.
## Show me the code!
C99 example:
```c
typedef struct {
float x, y;
} Position, Velocity;
void Move(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
}
int main(int argc, char *argv[]) {
ecs_world_t *ecs = ecs_init();
ECS_COMPONENT(ecs, Position);
ECS_COMPONENT(ecs, Velocity);
ECS_SYSTEM(ecs, Move, EcsOnUpdate, Position, Velocity);
ecs_entity_t e = ecs_new_id(ecs);
ecs_set(ecs, e, Position, {10, 20});
ecs_set(ecs, e, Velocity, {1, 2});
while (ecs_progress(ecs, 0)) { }
}
```
Same example in C++11:
```cpp
struct Position {
float x, y;
};
struct Velocity {
float x, y;
};
int main(int argc, char *argv[]) {
flecs::world ecs;
ecs.system<Position, const Velocity>()
.each([](Position& p, const Velocity& v) {
p.x += v.x;
p.y += v.y;
});
auto e = ecs.entity()
.set([](Position& p, Velocity& v) {
p = {10, 20};
v = {1, 2};
});
while (ecs.progress()) { }
}
```
## Projects using Flecs
If you have a project you'd like to share, let me know on [Discord](https://discord.gg/BEzP5Rgrrp)!
### Tempest Rising
https://store.steampowered.com/app/1486920/Tempest_Rising/
[![Tempest Rising](docs/img/projects/tempest_rising.png)](https://store.steampowered.com/app/1486920/Tempest_Rising/)
### Territory Control
https://store.steampowered.com/app/690290/Territory_Control_2/
[![image](docs/img/projects/territory_control.jpeg)](https://store.steampowered.com/app/690290/Territory_Control_2/)
### The Forge
https://github.com/ConfettiFX/The-Forge
[![image](docs/img/projects/the_forge.jpg)](https://github.com/ConfettiFX/The-Forge)
### Sol Survivor
https://nicok.itch.io/sol-survivor-demo
[![image](docs/img/projects/sol_survivor.png)](https://nicok.itch.io/sol-survivor-demo)
### Equilibrium Engine
https://github.com/clibequilibrium/EquilibriumEngine
[![image](docs/img/projects/equilibrium_engine.png)](https://github.com/clibequilibrium/EquilibriumEngine)
### Gravitas
https://thepunkcollective.itch.io/gravitas
[![image](docs/img/projects/gravitas.png)](https://thepunkcollective.itch.io/gravitas)
### After Sun
https://github.com/foxnne/aftersun
[![image](docs/img/projects/after_sun.png)](https://github.com/foxnne/aftersun)
### Tower defense (open source demo)
https://www.flecs.dev/tower_defense/etc ([repository](https://github.com/SanderMertens/tower_defense))
![image](docs/img/projects/tower_defense.png)
### Procedural City (open source demo)
https://www.flecs.dev/city ([repository](https://github.com/flecs-hub/city))
![image](docs/img/projects/city.png)
## Resources
### Resources provided by the community :heart:
- [Unreal Minimum Viable Flecs Project](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs)
- [Bgfx/Imgui module](https://github.com/flecs-hub/flecs-systems-bgfx/tree/bgfx_imgui)
- [Tower defense example](https://gist.github.com/oldmanauz/b4ced44737bf9d248233538fa06a989e)
- [Flecs + UE4 is magic](https://jtferson.github.io/blog/flecs_and_unreal/)
- [Quickstart with Flecs in UE4](https://jtferson.github.io/blog/quickstart_with_flecs_in_unreal_part_1/)
- [Automatic component registration in UE4](https://jtferson.github.io/blog/automatic_flecs_component_registration_in_unreal/)
- [Building a space battle with Flecs in UE4](https://twitter.com/ajmmertens/status/1361070033334456320)
- [Flecs + SDL + Web ASM example](https://github.com/HeatXD/flecs_web_demo) ([live demo](https://heatxd.github.io/flecs_web_demo/))
- [Flecs + Raylib example](https://github.com/Lexxicon/FlecsRaylib)
- [Flecs + gunslinger example](https://github.com/MrFrenik/gs_examples/blob/main/ex_demos/flecs/source/main.c)
- [Flecs based 3D game engine with editor](https://bit.ly/3T9cc1o)
### Flecs around the web
- [Discord](https://discord.gg/BEzP5Rgrrp)
- [Medium](https://ajmmertens.medium.com)
- [Twitter](https://twitter.com/ajmmertens)
- [Reddit](https://www.reddit.com/r/flecs)
## Flecs Hub
[Flecs Hub](https://github.com/flecs-hub) is a collection of repositories that show how Flecs can be used to build game systems like input handling, hierarchical transforms and rendering.
Module | Description
------------|------------------
[flecs.components.cglm](https://github.com/flecs-hub/flecs-components-cglm) | Component registration for cglm (math) types
[flecs.components.input](https://github.com/flecs-hub/flecs-components-input) | Components that describe keyboard and mouse input
[flecs.components.transform](https://github.com/flecs-hub/flecs-components-transform) | Components that describe position, rotation and scale
[flecs.components.physics](https://github.com/flecs-hub/flecs-components-physics) | Components that describe physics and movement
[flecs.components.geometry](https://github.com/flecs-hub/flecs-components-geometry) | Components that describe geometry
[flecs.components.graphics](https://github.com/flecs-hub/flecs-components-graphics) | Components used for computer graphics
[flecs.components.gui](https://github.com/flecs-hub/flecs-components-gui) | Components used to describe GUI components
[flecs.systems.transform](https://github.com/flecs-hub/flecs-systems-transform) | Hierarchical transforms for scene graphs
[flecs.systems.physics](https://github.com/flecs-hub/flecs-systems-physics) | Systems for moving objects and collision detection
[flecs.systems.sokol](https://github.com/flecs-hub/flecs-systems-sokol) | Sokol-based renderer
[flecs.game](https://github.com/flecs-hub/flecs-game) | Generic game systems, like a camera controller
## Language bindings
The following language bindings have been developed with Flecs! Note that these are projects built and maintained by helpful community members, and may not always be up to date with the latest commit from master!
- C#:
- [flecs-hub/flecs-cs](https://github.com/flecs-hub/flecs-cs)
- [BeanCheeseBurrito/Flecs.NET](https://github.com/BeanCheeseBurrito/Flecs.NET)
- Rust:
- [flecs-rs](https://github.com/jazzay/flecs-rs)
- [flecs-polyglot](https://github.com/flecs-hub/flecs-polyglot)
- Zig:
- [michal-z/zig-gamedev](https://github.com/michal-z/zig-gamedev/tree/main/libs/zflecs)
- [foxnne/zig-flecs](https://github.com/foxnne/zig-flecs)
- [prime31/zig-flecs](https://github.com/prime31/zig-flecs)
- Lua:
- [sro5h/flecs-luajit](https://github.com/sro5h/flecs-luajit)
- [flecs-hub/flecs-lua](https://github.com/flecs-hub/flecs-lua)

View File

@@ -0,0 +1,52 @@
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
new_git_repository(
name = "bake",
remote = "git@github.com:SanderMertens/bake.git",
commit = "cfc90745f9daa7b7fba80f229af18cdd5029b066",
shallow_since = "1614835160 -0800",
build_file_content = """
cc_library(
name = "driver-test",
visibility = ["//visibility:public"],
deps = [":util", ":bake"],
srcs = glob(["drivers/test/src/**/*.c", "drivers/test/src/**/*.h"]),
hdrs = glob(["drivers/test/include/**/*.h"]),
includes = ["drivers/test/include"],
)
cc_library(
name = "bake",
visibility = ["//visibility:public"],
deps = [":util"],
srcs = glob(["src/*.c", "src/*.h"]),
hdrs = glob(["include/*.h", "include/bake/*.h"]),
includes = ["include"],
)
cc_library(
name = "util",
visibility = ["//visibility:public"],
defines = ["__BAKE__", "_XOPEN_SOURCE=600"],
linkopts = select({
"@bazel_tools//src/conditions:windows": [],
"//conditions:default": ["-lrt -lpthread -ldl"],
}),
srcs = glob(["util/src/*.c"]) + select({
"@bazel_tools//src/conditions:windows": glob(["util/src/win/*.c"]),
"//conditions:default": glob(["util/src/posix/*.c"]),
}),
hdrs = glob(["util/include/*.h", "util/include/bake-util/*.h"]) + select({
"@bazel_tools//src/conditions:windows": glob(["util/include/bake-util/win/*.h"]),
"//conditions:default": glob(["util/include/bake-util/posix/*.h"]),
}),
includes = ["util/include"],
)
"""
)

View File

@@ -0,0 +1,48 @@
macro(list_targets RESULT)
file(GLOB_RECURSE FILES LIST_DIRECTORIES true RELATIVE ${CUR_DIR} *)
set(TARGET_LIST "")
foreach (FILE ${FILES})
if (IS_DIRECTORY ${CUR_DIR}/${FILE})
if (EXISTS ${CUR_DIR}/${FILE}/project.json)
message(STATUS "Found example ${FILE}")
list(APPEND TARGET_LIST ${FILE})
endif()
endif ()
endforeach ()
set(${RESULT} ${TARGET_LIST})
endmacro()
macro(create_target TARGET CONFIG)
if (CONFIG STREQUAL "")
set(TARGET_POSTFIX "")
else()
set(TARGET_POSTFIX "_${CONFIG}")
endif()
set(TARGET_NAME "${TARGET}${TARGET_POSTFIX}")
string(REPLACE "/" "_" TARGET_NAME "${TARGET_NAME}")
string(REPLACE "\\" "_" TARGET_NAME "${TARGET_NAME}")
set(TARGET_DIR "${CUR_DIR}/${TARGET}")
file(GLOB SRC ${TARGET_DIR}/src/*.c*)
add_executable(${TARGET_NAME} ${SRC})
target_include_directories(${TARGET_NAME} PUBLIC ${FLECS_DIR}/include)
target_include_directories(${TARGET_NAME} PUBLIC ${TARGET_DIR}/include)
target_link_libraries(${TARGET_NAME} flecs${TARGET_POSTFIX})
endmacro()
function(create_target_c TARGET CONFIG)
create_target("${TARGET}" "${CONFIG}")
target_default_compile_options_c(${TARGET_NAME})
target_default_compile_warnings_c(${TARGET_NAME})
endfunction()
function(create_target_cxx TARGET CONFIG)
create_target("${TARGET}" "${CONFIG}")
target_default_compile_options_cxx(${TARGET_NAME})
target_default_compile_warnings_cxx(${TARGET_NAME})
endfunction()

View File

@@ -0,0 +1,26 @@
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(FLECS_STRICT "Stricter warning options and Werror" OFF)
function(target_default_compile_options_c THIS)
set_target_properties(${THIS} PROPERTIES
LINKER_LANGUAGE C
C_STANDARD 99
C_STANDARD_REQUIRED ON
C_VISIBILITY_PRESET hidden)
endfunction()
function(target_default_compile_options_cxx THIS)
set_target_properties(${THIS} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON)
endfunction()

View File

@@ -0,0 +1,113 @@
function(target_default_compile_warnings_c THIS)
if (FLECS_STRICT)
if (CMAKE_C_COMPILER_ID STREQUAL "Clang"
OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
target_compile_options(${THIS} PRIVATE
$<$<CONFIG:Debug>:-Wshadow>
$<$<CONFIG:Debug>:-Wunused>
-Wall -Wextra -Werror
-Wcast-align
-Wpedantic
-Wconversion
-Wsign-conversion
-Wdouble-promotion
-Wno-missing-prototypes
-Wno-missing-variable-declarations)
elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(${THIS} PRIVATE
$<$<CONFIG:Debug>:-Wshadow>
$<$<CONFIG:Debug>:-Wunused>
-Wall -Wextra -Werror
-Wcast-align
-Wpedantic
-Wconversion
-Wsign-conversion
-Wdouble-promotion
-Wno-missing-prototypes
-Wno-missing-variable-declarations)
elseif (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${THIS} PRIVATE
/W3 /WX
/w14242 /w14254 /w14263
/w14265 /w14287 /we4289
/w14296 /w14311 /w14545
/w14546 /w14547 /w14549
/w14555 /w14619 /w14640
/w14826 /w14905 /w14906
/w14928)
else ()
message(WARNING
"No warning settings available for ${CMAKE_C_COMPILER_ID}. ")
endif ()
endif ()
endfunction()
function(target_default_compile_warnings_cxx THIS)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_compile_options(${THIS} PRIVATE
#$<$<CONFIG:RELEASE>:-Werror>
$<$<CONFIG:Debug>:-Wshadow>
$<$<CONFIG:Debug>:-Wunused>
-Wall -Wextra
-Wnon-virtual-dtor
-Wold-style-cast
-Wcast-align
-Woverloaded-virtual
-Wpedantic
-Wconversion
-Wsign-conversion
-Wdouble-promotion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${THIS} PRIVATE
#$<$<CONFIG:RELEASE>:-Werror>
$<$<CONFIG:Debug>:-Wshadow>
$<$<CONFIG:Debug>:-Wunused>
-Wall -Wextra
-Wnon-virtual-dtor
-Wold-style-cast
-Wcast-align
-Woverloaded-virtual
-Wpedantic
-Wconversion
-Wsign-conversion
-Wdouble-promotion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${THIS} PRIVATE
#$<$<CONFIG:RELEASE>:/WX>
/W3
/w14242 /w14254 /w14263
/w14265 /w14287 /we4289
/w14296 /w14311 /w14545
/w14546 /w14547 /w14549
/w14555 /w14619 /w14640
/w14826 /w14905 /w14906
/w14928)
else ()
message(WARNING
"No Warnings specified for ${CMAKE_CXX_COMPILER_ID}. "
"Consider using one of the following compilers: Clang, GNU, MSVC, AppleClang.")
endif ()
endfunction()

View File

@@ -0,0 +1,21 @@
codecov:
notify:
require_ci_to_pass: no
coverage:
precision: 2
round: down
range: "70...100"
status:
project: yes
patch: yes
changes: no
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no

View File

@@ -0,0 +1,175 @@
# Designing with Flecs
Designing games with ECS can be overwhelming and quite different from object oriented approaches, not to mention learning all the framework specific features! This guide provides a few quick tips for using the features provided by Flecs to build fast, readable and reusable code.
Note that these are my own guidelines, and as a result this is an opinionated document. There are many other approaches to designing with ECS, and it would be silly to claim that this is "the one true way". Take this with a grain of salt, and feel free to deviate when you feel it is appropriate.
One other note: this document is very light on feature documentation. The point of the document is to provide suggestions for how to design with Flecs features, not document their ins and outs. All of the features listed in this document are described in the manual and have example code.
## Entities
Entities are probably the easiest thing to get used to. They typically map to in-game objects the same way you would create them in other game engines.
### Entity Initialization
When creating entities, you typically want to initialize them with a set of default components, and maybe even default values. Flecs introduced prefabs for this use case. Prefabs let you create entity templates that contain components with default values. Creating entities from prefabs is not just an easy way to initialize entities, it is also faster, and helps with classifying the different kinds of entities you have.
### Entity Lifecycle
Entities can be created and deleted dynamically. When entities are deleted, the existing handles to that entity are no longer valid. When you are working with entity handles, it is often good practice to make sure they are still alive. You can do this with the `is_alive()` function. Other than that, entity handles are stable, which means that you can safely store them in your components or elsewhere.
### Entity Names
Flecs entities can be named. This makes it easy to identify entities in editors or while debugging, and also allows you to lookup entities by name. Names must be unique inside a scope, which is determined by the `ChildOf` relationship. For example, two entities with the same parent must have different names.
Names can be looked up using a relative path or absolute path using the `ecs_lookup_fullpath` or `entity::lookup` functions. The default scope separator for C applications is a dot (`.`), whereas in C++ it is a double colon (`::`). Lookups use a hashmap internally, which provides O(1) performance.
Entities can be assigned user friendly names with the doc addon, using the `ecs_doc_set_name` or `entity::set_doc_name` functions. Names assigned by the doc framework do not have to be unique.
## Components
Designing your components is probably the most important thing you will do in your ECS application. The reason is that if you change a component you have to update all systems that use it. Fortunately components can be designed in a way that minimizes refactoring and does not negatively impact performance.
### Component Size
The first tip is to try keep your components small and atomic. If you are choosing between a Transform component or separate Position, Rotation, Scale, Matrix components pick the latter. If you have a Turret component with a target and a rotation angle, split them up into two components.
The first reason behind this is that querying for multiple components adds minimal overhead, because most queries are cached.
The second reason is that it improves caching performance. If your system only needs Position, but also has to load all of the other data in Transform, you end up loading a lot of data in your cache that is not used. This means that useful data will get evicted from the cache more often, and that data needs to be loaded from RAM more often, which is much slower than when data is cached.
The third reason is that it minimizes refactoring. There are only so many ways to split up components, and infinitely more ways to combine them. Once components are designed as atomic units of data there aren't many reasons to combine them which results in less refactoring.
The fourth and last reason is that code using smaller components is more reusable. Reasons to combine two components may work well in one project, but not in another project. Atomic components are less opinionated since it's up to the project how they are combined in queries, which makes it more likely that they work well across projects. This trickles down to systems, as systems written for atomic components also end up being more reusable.
A disadvantage of small components is that you get more of them in a project. This can make it harder to find the components a system needs, especially in large projects with hundreds of components. Tools like https://www.flecs.dev/explorer/ can help with finding and documenting components in a project.
### Complex component data
There is a misconception that ECS components can only be plain data types, and should not have vectors, or more complex data structures. The reality is more nuanced. You may find yourself often needing specialized data structures, and it is perfectly fine to store these in components.
## Queries
Queries are the primary method in Flecs for finding the entities for a set of components (or more specifically: a component expression). Queries are easy to use, but there a few things to keep in mind.
### Use the right query
Flecs has cached queries and uncached queries. Cached queries (`ecs_query_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_filter_t`, `flecs::filter`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred.
Another difference is that uncached queries can be created from systems, while cached queries cannot. If you need a cached query in a system, it has to be created in advance and passed into the system, either by setting it as system context, adding a component to the system with the query, or passing it in the lambda capture list (C++ only). Systems themselves use cached queries.
Make sure to not repeatedly create and destroy cached queries! For more information, see [the query manual](Queries.md#types) for more details.
### Use in/inout/out annotations
Flecs analyzes how components are read and written by queries and uses this for things like change tracking and deciding when to merge command buffers. By default components are marked as `inout`. If a system only reads a component, make sure to mark it as `in`, as this can reduce the time spent by other parts of the application that rely on change detection and sync points.
For more information, see [the query manual](Queries.md#access-modifiers).
### Annotations
You can further annotate queries with components that are not matched with the query, but that are written using ECS operations (like add, remove, set etc.). Such operations are automatically deferred and merged at the end of the frame. With annotations you can enforce merging at an earlier point, by specifying that a component modification has been queued. When Flecs sees this, it will merge back the modifications before the next read.
See the sync point examples for more detail:
- C: https://github.com/SanderMertens/flecs/blob/master/examples/c/systems/sync_point
- C++: https://github.com/SanderMertens/flecs/blob/master/examples/cpp/systems/sync_point
## Systems
Designing systems is one of the hardest things to do when not coming from an ECS background. Object oriented code allows you to write logic that is local to a single object, whereas systems in ECS are ran for collections of similar objects. This requires a different approach towards design.
### System Scope
Try to design your systems with a single responsibility. This can sometimes be difficult, especially if you are building new features and are not exactly sure yet what the end result will look like. That is fine. It is better to start with something that works, and refine it afterwards. If you find yourself with a system that does a lot of things, don't worry. Because systems in ECS are decoupled from everything else, it is generally pretty easy to split them up.
While it is perfectly fine to have large systems in an ECS application, there are a few advantages to keeping them small:
- Smaller systems make it easier to isolate behavior, as you can simply remove systems from your application that you don't want to test.
- Smaller systems usually have less complicated code, which can allow for more compiler optimizations (like auto vectorization).
- Smaller systems are easier to reuse across project.
### System Scheduling
Flecs has the ability to run and schedule your systems for you. The advantage of this, versus manually listing and running systems, is that it is easier to import systems from multiple, reusable modules.
A system is basically a combination of three things: a query, a function, and ordering information. The query finds the right entities, and the function is invoked with the matched entities. The ordering information makes sure that the system is inserted in the right point in your frame. If you get this right, you can import any number of systems into your project, and you can just run them without spending any time on manually sorting them out.
The ordering information consists out of a phase (see phases and pipelines) and an implicit declaration order. Systems are ordered according to their phases first. Within a phase, they are ordered by declaration order. This may feel rigid, but is very deliberate. It prevents you from defining dependencies between systems, which can make it difficult to reuse systems across projects.
On the other hand, if you are working with an existing framework or engine, you may not have the luxury of scheduling everything yourself. The engine may for example provide you with callbacks in which you need to do certain logic. Maybe you want to build your own threading system. In those situations it can make sense to take control of running systems yourself.
Sometimes you may even not use systems at all, and just run queries. In this case you may want to disable the system addon (see the addsons section in the README). Note that some Flecs features depend on systems, like the REST API and timers.
## Phases and Pipelines
Phases and pipelines are the primitives that Flecs uses to order systems. A pipeline is a set of ordered phases. Systems can be assigned to those phases. When using phases and pipelines correctly, it allows you to write plug & play systems that are easy to reuse in different projects.
### Selecting a Phase
When you create a system, you can assign a phase to it. By default, that phase is `OnUpdate`. Flecs comes with a whole bunch of phases though, and just looking at the whole list can feel a bit overwhelming:
- `OnLoad`
- `PostLoad`
- `PreUpdate`
- `OnUpdate`
- `OnValidate`
- `PostUpdate`
- `PreStore`
- `OnStore`
So what do these all mean? Actually they mean nothing at all! They are just tags you can assign to systems, and those tags ensure that all systems in, say, the `PreUpdate` phase are executed _before_ the systems in the `OnUpdate` phase. What is also important to realize is that this list of phases is only the default provided by Flecs. Maybe your project needs only half of those, or maybe it needs entirely different ones! Flecs lets you specify your custom phases to match your project needs.
There are some conventions around the builtin phases, and following them helps to ensure that your code works well with the Flecs module ecosystem. Here they are:
### OnLoad
This phase contains all the systems that load data into your ECS. This would be a good place to load keyboard and mouse inputs.
### PostLoad
Often the imported data needs to be processed. Maybe you want to associate your keypresses with high level actions rather than comparing explicitly in your game code if the user pressed the 'K' key. The PostLoad phase is a good place for this.
### PreUpdate
Now that the input is loaded and processed, it's time to get ready to start processing our game logic. Anything that needs to happen after input processing but before processing the game logic can happen here. This can be a good place to prepare the frame, maybe clean up some things from the previous frame, etcetera.
### OnUpdate
This is usually where the magic happens! This is where you put all of your gameplay systems. By default systems are added to this phase.
### OnValidate
This phase was introduced to deal with validating the state of the game after processing the gameplay systems. Sometimes you moved entities too close to each other, or the speed of an entity is increased too much. This phase is for righting that wrong. A typical feature to implement in this phase would be collision detection.
### PostUpdate
When your game logic has been updated, and your validation pass has ran, you may want to apply some corrections. For example, if your collision detection system detected collisions in the OnValidate phase, you may want to move the entities so that they no longer overlap.
### PreStore
Now that all of the frame data is computed, validated and corrected for, it is time to prepare the frame for rendering. Any systems that need to run before rendering, but after processing the game logic should go here. A good example would be a system that calculates transform matrices from a scene graph.
### OnStore
This is where it all comes together. Your frame is ready to be rendered, and that is exactly what you would do in this phase.
That was a quick overview of all the builtin phases. Note that these are just guidelines! Feel free to deviate if your project calls for it.
### Custom phases and pipelines
An application can add phases to the existing list, or define a pipeline from scratch. See the following examples on how to do this:
- C:
- https://github.com/SanderMertens/flecs/tree/master/examples/c/systems/custom_phases
- https://github.com/SanderMertens/flecs/tree/master/examples/c/systems/custom_pipeline
- C++:
- https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/custom_phases
- https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/custom_pipeline
## Modules
Large applications can often contain many components and systems. Some large commercial projects have reported up to 800 components! Managing all those components and systems becomes important on that scale, and that is what modules are for. Modules are one of those features that you usually don't think about when selecting an ECS, but they can make your life a lot easier.
### Defining Modules
The purpose of modules is really to enable reusability. A well written module can be imported into any project, and will do its thing without any tweaking or tinkering. To achieve this, make sure to define your modules around features. Features seldomly consist out of a single system or component, but can have many. Examples are rendering, collision detection, input management, and so on.
### Module Dependencies and Ordering
Modules can depend on each other. In fact, often do! Importing a module twice has no penalties in Flecs, it will not define your systems and components twice. This enables your application code to import the modules it needs, without having to worry about whether they were already loaded.
The order in which you import dependencies in a module is important. If you define systems before the import, those systems will be ran _before_ the systems from the imported module _within the same phase_ (read this line a few times until you understand, it's important). This allows you some degree of flexibility around how systems from different modules should be scheduled.
What is key to note here is that the granularity of control is at the module level, _never_ at the individual system level. The reason for this is that modules may reimplement their features with different systems. If you have inter-system dependencies, those could break easily every time you update a module. This also makes sure that you can replace one module for another without running into annoying compatibility issues.
### Modules and Feature Swapping
A good practice to employ with modules is to split them up into components.* modules and systems.* modules. For example, you may have a module `components.physics` that contains all the components to store data for a physics system. You may then have a module called `systems.physics`, which imports the components module and "implements" the components. Because applications only have access to `components.physics` (they import it as well) but do not have direct access to the systems inside `systems.physics`, you can simply swap one physics implementation with another without changing application code.
This is a powerful pattern enabled by ECS that will give your projects a lot of flexibility and freedom in refactoring code.
### Module Overhead
You might wonder whether a module with lots of systems, of which only a few are used by your application makes your application slower. The answer is fortunately no. Flecs only inserts systems into the main loop that have matched with actual entities. Any imported systems that have never matched with anything remain dormant, and will not negatively affect performance.
## Relationships
When you are working with Flecs, chances are that at some point you'll want to use relationships. The two most common uses for relationships are hierarchies like a scene graph (the `ChildOf` relationship) and prefabs (the `IsA` relationship).
In many cases you might want to use your own relationships. Here are a few signs to look out for that can tell you to think about relationships:
- You have a component with an entity handle, and you need to find all entities that point to a specific entity.
- You have many components with the same or similar set of members, and systems that duplicate code for each component.
- You need to store multiple instances of the same component on an entity.
- You're designing some kind of container structure, like an inventory.
- You are looking to group entities by something like world cells or layers, and want to be able to lookup all entities for a cell.
- You're adding an enumeration type as component, but want to query for enumeration constants.
See the [relationships blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) and [relationships manual](Relationships.md) for more information.

View File

@@ -0,0 +1,37 @@
# Documentation
## Getting Started
- [FAQ](/flecs/md_docs_FAQ.html)
- [Quickstart](/flecs/md_docs_Quickstart.html)
- [Flecs Script Tutorial](/flecs/md_docs_FlecsScriptTutorial.html)
- [Designing with Flecs](/flecs/md_docs_DesignWithFlecs.html)
- [Getting Started with Unreal Engine](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs)
## Manuals
- [Manual](/flecs/md_docs_Manual.html)
- [Query Manual](/flecs/md_docs_Queries.html)
- [Systems Manual](/flecs/md_docs_Systems.html)
- [Relationships Manual](/flecs/md_docs_Relationships.html)
- [JSON Format Manual](/flecs/md_docs_JsonFormat.html)
- [REST API Manual](/flecs/md_docs_RestApi.html)
## API reference
- [C API](/flecs/group__c.html)
- [C++ API](/flecs/group__cpp.html)
## Examples
- [C examples](https://github.com/SanderMertens/flecs/tree/master/examples/c)
- [C++ examples](https://github.com/SanderMertens/flecs/tree/master/examples/cpp)
## Demos
- [Tower Defense (C++)](https://github.com/SanderMertens/tower_defense)
- [City (C)](https://github.com/flecs-hub/city)
## Articles
- [Where are my entities and components](https://ajmmertens.medium.com/building-an-ecs-1-where-are-my-entities-and-components-63d07c7da742)
- [Archetypes and vectorization](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9)
- [Making the most of entity identifiers](https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647)
- [Building games in ECS with entity relationships](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c)
- [Why storing state machines in ECS is a bad idea](https://ajmmertens.medium.com/why-storing-state-machines-in-ecs-is-a-bad-idea-742de7a18e59)
- [Why vanilla ECS is not enough](https://ajmmertens.medium.com/why-vanilla-ecs-is-not-enough-d7ed4e3bebe5)
- [ECS: From tool to paradigm](https://ajmmertens.medium.com/ecs-from-tool-to-paradigm-350587cdf216)

View File

@@ -0,0 +1,133 @@
# FAQ
Frequently asked questions.
## What is an ECS?
See the [ECS FAQ](https://github.com/SanderMertens/ecs-faq)
## Why is Flecs written in C?
There are a lot of reasons, but the main ones are:
- Faster compile times, especially when compared with header-only C++ libraries
- C can be called from almost any programming language
- C is super portable and has a tiny runtime so you can run it on a toaster if you want
- The rules and limitations of the ECS aren't dictated by any type system
- You can create a zero-overhead C++ API on top of C, but not the other way around
## Can I use Flecs with C++14 or higher?
You can! Even though the C++ API is C++11, you can use it with any revision at or above 11.
## Can I use std::vector or other types inside components?
You can! Components can contain almost any C++ type.
## What is an archetype?
Archetype-based refers to the way the ECS stores components. It means that all entities with the same components are stored together in an archetype. This provides efficient CPU cache utilization and allows for things like vectorization and fast querying.
Other examples of archetype implementations are Unity DOTS, Unreal Mass and Bevy ECS.
For more information, see this blog: https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9
## How does Flecs compare with EnTT?
Flecs and EnTT both are ECS libraries, but other than that they are different in almost every way, which can make comparing the two frameworks tricky. When you are comparing Flecs and EnTT, you can _generally_ expect to see the following:
- Add/remove operations are faster in EnTT
- Single component queries are faster in EnTT
- Multi-component queries are faster in Flecs
- Bulk-creating entities are faster in Flecs
- Entity destruct are faster in Flecs (especially for worlds/registries with lots of components)
- Iterating a single entity's components are faster in Flecs
When doing a benchmark comparison don't rely on someone elses numbers, always test for your own use case!
## Is Flecs used for commercial projects?
Yes, Flecs is used commercially.
## Why are my queries so slow?
This is likely because you're creating a query in a loop. Queries should be created once, and iterated often.
## Can Flecs be compiled to web assembly?
Yes it can! See the [quickstart manual](Quickstart.md) for more information.
## Why am I getting an “use of undeclared identifier 'FLECS_ID …’” compiler error?
This happens in C if the variable that holds the component id can't be found. This example shows how to fix it: https://github.com/SanderMertens/flecs/tree/master/examples/c/entities/fwd_declare_component
## Why are my entity ids so large?
When you inspect the integer value of an entity you may see that this value is very large, usually around 4 billion. This is not a bug, and instead means that the entity id has been recycled. This example shows when recycling happens:
```cpp
flecs::entity e1 = world.entity(); // small id
e1.destruct(); // id is made available for recycling
flecs::entity e2 = world.entity(); // recycles e1
e1.is_alive(); // false
e2.is_alive(); // true
std::cout << e2.id(); // large id, upper 32 bits contains liveliness count
```
## What is the difference between add & set? Why do both exist?
An `add` just adds a component without assigning a value to it. A `set` assigns a value to the component. Both operations ensure that the entity will have the component afterwards.
An `add` is used mostly for adding things that don't have a value, like empty types/tags and most relationships. If `add` is used with a component that has a constructor, adding the component will invoke its constructor.
Additionally you can use `emplace` to construct a component in place in the storage (similar to `std::vector::emplace`).
## Can Flecs serialize components?
Yes it can! See the reflection examples:
- https://github.com/SanderMertens/flecs/tree/master/examples/c/reflection
- https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection
## Why is Flecs so large?
If you look at the size of the flecs.c and flecs.h files you might wonder why they are so large. There are a few reasons:
- The files contain a _lot_ of comments and in-code documentation!
- Flecs has a small core with a lot of addons. The flecs.c and flecs.h files are the full source code, including addons.
- The flecs.h file contains the full C++ API.
- Flecs implements its own data structures like vectors and maps, vs. depending on something like the STL.
- C tends to be a bit more verbose than other languages.
Not all addons are useful in any project. You can customize a Flecs build to only build the things you need, which reduces executable size and improves build speed. See the quickstart on how to customize a build.
## Why does the explorer not work?
Make sure that:
- The REST API is enabled (see the [REST manual](https://www.flecs.dev/flecs/md_docs_RestApi.html))
- You can reach the REST API by testing http://localhost:27750/entity/flecs
- You call `ecs_progress`/`world::progress` in your main loop
If that doesn't work, see the [README of the explorer](https://github.com/flecs-hub/explorer) for potential issues with browser security settings and how to run a local instance of the explorer.
## Does the explorer collect data from my application?
No! The https://flecs.dev/explorer page is a 100% client side application that does not talk to a backend. When you navigate to the page it will attempt to connect to http://localhost:27750 to find a running Flecs application. The data that the explorer fetches never leaves your machine.
## Why can't I see component values in the explorer?
The explorer can only display component values if the reflection data has been registered for the component. See the [C reflection examples](https://github.com/SanderMertens/flecs/tree/master/examples/c/reflection) and [C++ reflection examples](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection) for more information.
## How do I detect which entities have changed?
Flecs has builtin change detection. Additionally, you can use an `OnSet` observer to get notified of changes to component values. See the query change detection and observer examples for more information.
## Are relationships just a component with an entity handle?
No, relationships are a deeply integrated feature that is faster in many ways than a component with an entity handle. See the [relationship manual](Relationships.md) for more information.
## Can I create systems outside of the main function?
You can! Systems can be created from any function, except other systems.
## Can I use my own scheduler implementation?
You can! The flecs scheduler (implemented in the pipeline addon) is fully optional and is built on top of Flecs. You can create a custom pipeline, or a custom schedule implementation that does something else entirely than what Flecs provides.
## Can I use Flecs without using systems?
You can! Systems are an optional addon that can be disabled. You can build applications that just use Flecs queries.
## Why does the lookup function not find my entity?
This is likely because the entity has a parent. A lookup by name requires you to provide the full path to an entity, like:
```c
ecs_lookup_fullpath(world, "parent.child");
```
or in C++:
```cpp
world.lookup("parent::child");
```
## Can I add or remove components from within a system?
You can! By default ECS operations are deferred when called from inside a system, which means that they are added to a queue and processed later when it's safe to do so. Flecs does not have a dedicated command API, if you call an operation from a system it will be automatically added to the command queue!

View File

@@ -0,0 +1,769 @@
# Flecs Script Tutorial
This tutorial shows you how to use Flecs script, which is a declarative language for creating entities without having to write and compile code. Flecs script can be used for different things, such as building scenes, assets, or as a language for configuration files.
In this tutorial we will be designing a simple fence asset from scratch, and make it parameterizable so it can be easily reused across scenes. By the end of the tutorial we'll have an asset that looks like this:
[![preview of fence asset](img/script_tutorial/tut_playground_preview.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A)
Note: in order to use Flecs script an app needs to be built with the `FLECS_PLECS` addon.
## Getting Started with the Explorer
The Flecs explorer is a web-based application that lets us write scripts and see the results directly. In the tutorial we will use the explorer in combination with the Flecs playground, which has a rendering canvas and comes preloaded with a number of modules and assets.
Go to this URL to open the explorer with the playground:
https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js
The page should look similar to this:
[![explorer with playground](img/script_tutorial/tut_playground.png)](https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js)
The panel on the left is the entity treeview, which shows all of the entities in our scene. The center view is the canvas, which shows us the renderable entities of our scene (more on that later). Finally, the pane on the right is the editor, where we can write flecs scripts.
At any point in time you can disable panels by clicing on the "x" in the top-right corner. Panels can be brought back by pressing on their button in the menu bar on the left.
One other important thing is the link button in the top-right of the screen. You can use that button to obtain a link to your content, which is a good way to save progress, or to share what you've created with other people.
## The Basics
Currently the explorer is showing the default scene. Let's clear it by removing all code from the editor. You should now see an empty canvas:
![explorer with empty canvas](img/script_tutorial/tut_playground_empty.png)
Lets create an entity by typing its name into the editor:
```js
my_entity
```
Notice that as we are typing the entity shows up in the treeview:
[![explorer with a single entity](img/script_tutorial/tut_playground_entity.png)](https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js&script=%0Amy_entity%0A)
Entities are automatically created if they did not exist yet. Try entering the same entity twice:
```js
my_entity
my_entity
```
Only one entity shows up in the treeview. The second time `my_entity` was parsed it already existed, so nothing needed to be done.
## Adding Components
Now that we have an entity, let's add a few components and tags to it. Change the text in the editor to this, to create an entity with a tag called `SpaceShip`:
```js
my_entity :- SpaceShip
```
Note that we didn't have to explicitly declare `SpaceShip` in advance, and that it also shows up as entity in the treeview. We can add multiple things to an entity this way:
```js
my_entity :- SpaceShip
my_entity :- FasterThanLight
```
To avoid repeating the entity name many times, we can use the `{}` operators to open a scope for the entity. Inside the scope we can list components for the entity by prefixing them with a dash (`-`):
```js
my_entity {
- SpaceShip
- FasterThanLight
}
```
We can inspect the entity and its contents by opening it in the entity inspector. To do this, click on the entity in the treeview. You should now see this:
[![entity with two tags](img/script_tutorial/tut_playground_inspector.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%7D%0A&entity=my_entity)
Note how the `SpaceShip` and `FasterThanLight` tags show up in the editor. There is also a `Script: main` tag, which exists for Flecs to keep track of which entities got created by our script.
Adding a component is similar to adding tag with a value. Let's add the `Position3` component from the `flecs.components.transform` module which comes preloaded with the playground. Note how it also shows up in the inspector when we add this code:
```js
my_entity {
- SpaceShip
- FasterThanLight
- flecs.components.transform.Position3{1, 2, 3}
}
```
Having to type the full module name each time we want to use the `Position3` component would be annoying. Add this line to the top of the script:
```js
using flecs.components.*
```
We can now use the component without the module name, which looks much cleaner:
```js
my_entity {
- SpaceShip
- FasterThanLight
- Position3{1, 2, 3}
}
```
If all went well, the playground should now look like this:
[![entity with two tags and a component](img/script_tutorial/tut_playground_component.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%20%20-%20Position3%7B1%2C%202%2C%203%7D%0A%7D%0A&entity=my_entity)
Note how after we added the `Position3` component, the inspector also shows the `Transform` and `WorldCell` components. This happens because the playground imports modules that implement world partitioning and transforms, which we get for free just by using `flecs.components.transform.Position3` component.
In addition to components and tags we can also add relationship pairs to entities. To add a pair, add this line to the scope of the entity, and note how it shows up in the inspector:
```js
- (OwnedBy, Player)
```
Entities can be created in hierarchies. A child entity is created in the scope of an entity just like the components and tags, but without the preceding `-`. Add this to the scope of the entity:
```js
cockpit {
pilot :- (Faction, Earth)
}
```
You can see the hierarchy this created in the treeview by expanding `my_entity`:
[![entity with hierarchy](img/script_tutorial/tut_playground_hierarchy.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%20%20-%20Position3%7B1%2C%202%2C%203%7D%0A%20%20%0A%20%20cockpit%20%7B%0A%20%20%20%20pilot%20%3A-%20(Faction%2C%20Earth)%0A%20%20%7D%0A%7D%0A&entity=my_entity)
Congratulations! You now know how to create entities, hierarchies, and how to add components and tags. None of the entities we created so far are visible in the canvas however, so lets do something about that.
## Drawing Shapes
We will be building a fence asset from just primitive shapes, where each entity is a single shape (note that in a real game you would likely want to use actual meshes).
The renderer uses regular ECS queries to find the entities to render. For our entities to show up in these queries, they need to have at least three components:
- `Position3`
- `Rgb` (a color)
- `Box` or `Rectangle`
Let's start by drawing a plane. First remove all code from the editor except for this line:
```js
using flecs.components.*
```
Now add these lines into the editor to create our ground `plane`:
```js
plane {
- Position3{}
- Rectangle{100, 100}
- Rgb{0.9, 0.9, 0.9}
}
```
Something happened! But it doesn't look quite right:
[![a buggy ground plane](img/script_tutorial/tut_playground_plane_wrong.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rectangle%7B100%2C%20100%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A)
The rectangle is rotated the wrong way for our plane. To fix this we need to rotate it 90 degrees or `π/2` radians on the x axis. First lets define `π` as a constant value in our script:
```js
const PI = 3.1415926
```
Now add this line to the scope of `plane`:
```js
- Rotation3{$PI / 2}
```
That looks better:
[![a ground plane](img/script_tutorial/tut_playground_plane_rotated.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B100%2C%20100%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A)
Let's increase the sides of the plane to `10000` so that the fog effect makes it blends in with the background, which gives the illusion of a horizon:
[![a ground plane](img/script_tutorial/tut_playground_plane.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A)
Note that the `PI` variable does not show up in the treeview. Variables do not create entities, and only exist within the context of a script.
Let's now add a cube to the scene. The code for this looks similar to the plane:
```js
box {
- Position3{}
- Box{10, 10, 10}
- Rgb{1, 0, 0}
}
```
The box is showing up, but it's intersecting with our plane:
[![a cube](img/script_tutorial/tut_playground_half_box.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Abox%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Box%7B10%2C%2010%2C%2010%7D%0A%20%20-%20Rgb%7B1%2C%200%2C%200%7D%0A%7D%0A)
To fix this, we can move it up by setting the `y` member of `Position3` to half its size:
```js
box {
- Position3{y: 5}
- Box{10, 10, 10}
- Rgb{1, 0, 0}
}
```
Now the entire cube is visible, and should look like this:
[![a cube](img/script_tutorial/tut_playground_box.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Abox%20%7B%0A%20%20-%20Position3%7By%3A%205%7D%0A%20%20-%20Box%7B10%2C%2010%2C%2010%7D%0A%20%20-%20Rgb%7B1%2C%200%2C%200%7D%0A%7D%0A)
We now have all of the basic knowledge to start drawing a fence! Note that if you want to move the camera around, first click on the canvas to give it focus. You can now move the camera around. To release focus from the canvas, click on it again (the green border should disappear).
## Drawing a Fence
To draw a fence we just need to combine differently sized boxes together. First remove the code for the cube from the editor.
Before drawing the boxes, lets first make sure they're all created with the same color. We could add a color component with the same values to each entity, but that gets tedious if we want to change the color. Instead, we can use the `with` statement:
```js
with Rgb{0.15, 0.1, 0.05} {
// Boxes go here
}
```
Inside the `with` statement we can now create two box entities for the left and right pillar of the fence:
```js
with Rgb{0.15, 0.1, 0.05} {
left_pillar {
- Position3{x: -10, y: 5}
- Box{2, 10, 2}
}
right_pillar {
- Position3{x: 10, y: 5}
- Box{2, 10, 2}
}
}
```
The playground should now look like this:
[![two pillars](img/script_tutorial/tut_playground_pillars.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Awith%20Rgb%7B0.15%2C%200.1%2C%200.05%7D%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-10%2C%20y%3A%205%7D%0A%20%20%20%20-%20Box%7B2%2C%2010%2C%202%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%2010%2C%20y%3A%205%7D%0A%20%20%20%20-%20Box%7B2%2C%2010%2C%202%7D%0A%20%20%7D%0A%7D%0A)
That works, but the code is starting to look a bit unwieldy. There are lots of magic numbers which will become difficult to maintain as our asset gets more complex. Let's see if we can clean this up a bit.
First lets define two variables for the color and box shape of the pillars. We already saw an example of a variable when we defined `PI`. These looks similar, except that because they are composite values, we also define their type:
```js
const color : Rgb = {0.15, 0.1, 0.05}
const pillar_box : Box = {2, 10, 2}
```
This is better, but `pillar_box` still contains values that we have to update each time we want to change the shape of our fence. Instead we can do this, which is a bit more typing now but will be easier to maintain later:
```js
const height = 10
const color : Rgb = {0.15, 0.1, 0.05}
const pillar_width = 2
const pillar_box : Box = {
$pillar_width,
$height,
$pillar_width
}
```
Let's also add an additional `width` variable which stores the width of the fence (or the distance between two pillars):
```js
const width = 20
```
We can now update the code that creates the pillars to this:
```js
with $color, $pillar_box {
left_pillar {
- Position3{x: -$width/2, y: $height/2}
}
right_pillar {
- Position3{x: $width/2, y: $height/2}
}
}
```
This will reproduce the exact same scene, but we can now tweak the variables to change the shape of our fence. Try playing around with the values of the different variables to see their effect!
[![two modified pillars](img/script_tutorial/tut_playground_vars.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2010%0Aconst%20height%20%3D%2015%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%204%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A)
This is no fence yet. We're still missing the crossbars. Let's add another entity to see what it looks like:
```js
bar {
- Position3{y: $height / 2}
- Box{$width, 2, 1}
- $color
}
```
[![two pillars and a bar](img/script_tutorial/tut_playground_bar.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Abar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%20%2F%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0A)
Let's add a second entity and space the two entities out a bit so they don't overlap. Also make sure to give both entities a different name, or else we'll end up overwriting the previous values:
```js
top_bar {
- Position3{y: $height/2 + 2}
- Box{$width, 2, 1}
- $color
}
bottom_bar {
- Position3{y: $height/2 - 2}
- Box{$width, 2, 1}
- $color
}
```
[![a simple fence](img/script_tutorial/tut_playground_fence.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Atop_bar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0Abottom_bar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%2F2%20-%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0A)
That starts to look more like a fence! Just like before however, we have a bunch of magic values and redundancies, but we now know how to fix that. First, let's define a `bar_sep` variable to specify how far apart the bars should be separated:
```js
const bar_sep = 4
```
Let's also create a variable for the bar shape and bar height, similar to what we did before. Let's make the bar thickness scale with the width of a pillar:
```js
const bar_height = 2
const bar_depth = $pillar_width/2
const bar_box : Box = {$width, $bar_height, $bar_depth}
```
With that in place, we can cleanup the entity code for the bars:
```js
with $color, $bar_box {
top_bar {
- Position3{y: $height/2 + $bar_sep/2}
}
bottom_bar {
- Position3{y: $height/2 - $bar_sep/2}
}
}
```
We now have our fence, which we can fully tweak with by changing the values of the variables! There are a few problems with the current code however:
- The code is not packaged in a way that we can easily use
- We can make the fence very wide or very high, but it will end up looking weird. Ideally we increase the number of pillars and bars as we scale up the fence.
The code is also growing. To make sure you don't lose track of what you're doing you can add comments:
```js
// Create two bar entities
with $color, $bar_box {
top_bar {
- Position3{y: $height/2 + $bar_sep/2}
}
bottom_bar {
- Position3{y: $height/2 - $bar_sep/2}
}
}
```
## Prefabs
We have the basic structure of a fence, but what if we wanted two fences? We could of course duplicate all of the code, but that would be impractical. Instead we can package up what we have in a _prefab_.
Turning the existing code into a prefab is easy. Simply add these lines around the fence code:
```js
Prefab Fence {
// fence code goes here
}
```
This creates an entity `Fence` with the tag `Prefab`, and stores all entities we created so far as children of the `Fence` entity. This is the same as running the following code, but a bit more convenient:
```js
Fence {
- Prefab
// fence code goes here
}
```
Once you put the fence entities inside the prefab scope, you'll notice a few things:
- The fence disappears from the canvas
- The fence entities now show up under `Fence` in the treeview
[![a fence prefab hierarchy](img/script_tutorial/tut_playground_prefab.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Aconst%20bar_sep%20%3D%204%0Aconst%20bar_height%20%3D%202%0Aconst%20bar_depth%20%3D%20%24pillar_width%2F2%0Aconst%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24width%2C%20%0A%20%20%24bar_height%2C%20%0A%20%20%24bar_depth%0A%7D%0A%0Awith%20%24color%2C%20%24bar_box%20%7B%0A%20%20top_bar%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%7D%0A%20%20bottom_bar%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%7D%0A%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A)
The reason that the fence disappears is that `Prefab` is a builtin tag that is by default ignored by queries, and as a result also by the renderer. Children of a prefab also become prefabs, so they also get ignored.
That seems a bit underwhelming, but what we can now do is create an entity that _inherits_ from the `Fence` prefab, by adding this line to the bottom of the script:
```
my_fence : Fence
```
[![a fence prefab instance](img/script_tutorial/tut_playground_instance.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Amy_fence%20%3A%20Fence%0A)
Great, the fence is back! Even better, we can now create two fences simply by instantiating the prefab twice. Just make sure to give both instances different positions:
```js
fence_a : Fence {
- Position3{-10}
}
fence_b : Fence {
- Position3{10}
}
```
[![two fence prefab instances](img/script_tutorial/tut_playground_two_instances.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A)
We could save this script as is, load it into our game, and instantiate the prefab from regular C/C++ code. This is what that would look like:
```c
// In C
ecs_plecs_from_file(world, "fence.flecs");
ecs_entity_t fence = ecs_lookup_fullpath(world, "Fence");
ecs_entity_t fence_a = ecs_new_w_pair(world, EcsIsA, fence);
ecs_entity_t fence_b = ecs_new_w_pair(world, EcsIsA, fence);
ecs_set(world, fence_a, EcsPosition3, {-10});
ecs_set(world, fence_b, EcsPosition3, {10});
```
```cpp
// In C++
using namespace flecs::components::transform;
ecs_plecs_from_file(world, "fence.flecs");
auto fence = world.lookup("Fence");
auto fence_a = world.entity().is_a(fence);
auto fence_b = world.entity().is_a(fence);
fence_a.set<Position3>({-10});
fence_b.set<Position3>({10});
```
We still have some problems, which we'll address in the next section:
- Our prefab is static, we cannot change the parameters of the fence
- The pillars and bars don't increase when the fence size increases
## Assemblies
Prefabs are static collections of entities and components that we can instantiate multiple times. Assemblies are _prefab compositions_ that can be parameterized. In other words, assemblies let us to create our Fence, while also specifying the width and height.
Changing the above code into an assembly is easy. Just change this line:
```js
Prefab Fence {
// fence code
}
```
into this:
```js
assembly Fence {
// fence code
}
```
The editor will throw the following error:
```
assembly 'Fence' has no properties
```
[![a fence assembly](img/script_tutorial/tut_playground_assembly_error.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A)
What does this mean? Assemblies, unlike prefabs, need _properties_, which are like the inputs to the assembly. For our fence, these could be `width` and `heigth` values.
To add properties to the assembly, we can take some of our existing `const` variables, and change `const` to `prop`. This will make the variables visible to the outside. Another thing we must do for properties is explicitly specify their type.
Let's take the existing `width`, `height` and `color` variables, and change them to properties. It is also good practice to put the props at the top of the assembly code, so it's easy to see which inputs it has:
```js
assembly Fence {
prop width : flecs.meta.f32 = 20
prop height : flecs.meta.f32 = 10
prop color : Rgb = {0.15, 0.1, 0.05}
// fence code
}
```
We can get rid of the `flecs.meta.` prefix by adding this to the top of our script:
```js
using flecs.meta
```
The code can now be changed to this:
```js
assembly Fence {
prop width : f32 = 20
prop height : f32 = 10
prop color : Rgb = {0.15, 0.1, 0.05}
// fence code
}
```
Note that while we were doing this, the fence disappeared again from the canvas. This happened because while we _inherit_ a prefab, we _assign_ an assembly. To make the fences visible again, change the code that creates the fences to this:
```js
fence_a {
- Fence{}
- Position3{-10}
}
fence_b {
- Fence{}
- Position3{10}
}
```
The fences are back, with the default values we provided to our props. But we now have a new power! Try changing it to this:
```js
fence_a {
- Fence{width: 10, height: 20}
- Position3{-10}
}
fence_b {
- Fence{width: 25, height: 10}
- Position3{10}
}
```
[![two instantiated fence assemblies](img/script_tutorial/tut_playground_assembly.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2020%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2010%2C%20height%3A%2020%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2025%2C%20height%3A%2010%7D%0A%20%20-%20Position3%7B10%7D%0A%7D%0A)
We are now well on our way to define a procedural fence. Note how similar assigning an assembly looks to assigning a component. This is no coincidence: an assembly is translated to a component, where each property becomes a member of that component.
This is something we can exploit in C/C++ code to make assigning assemblies to entities as easy as assigning a component:
```c
// In C
typedef struct {
float width;
float height;
EcsRgb color;
} Fence;
// Register a regular component
ECS_COMPONENT(world, Fence);
// Because the Fence assembly has the same name as the
// component it will "bind" to it.
ecs_plecs_from_file(world, "fence.flecs");
// Set the component as usual
ecs_entity_t fence_a = ecs_set(world, 0, Fence, {10, 20});
ecs_entity_t fence_b = ecs_set(world, 0, Fence, {25, 10});
ecs_set(world, fence_a, EcsPosition3, {-10});
ecs_set(world, fence_b, EcsPosition3, {10});
```
```cpp
// In C++
using namespace flecs::components::transform;
struct Fence {
float width;
float height;
flecs::components::graphics::Rgb color;
}
// Because the Fence assembly has the same name as the
// component it will "bind" to it.
ecs_plecs_from_file(world, "fence.flecs");
auto fence_a = world.entity().set<Fence>({10, 20});
auto fence_b = world.entity().set<Fence>({25, 10});
fence_a.set<Position3>({-10});
fence_b.set<Position3>({10});
```
For this to work, we have to make sure that the types and ordering of the properties in the script match up with the types and ordering of members in the component type.
## Grids
Let's now see if we can change the number of pillars and bars depending on the fence width and height.
At first glance this sounds like something that would require loops, and those are not supported in Flecs script. There is a way around this however, which is to use a `Grid` component that does the instantiating for us.
The `Grid` component is provided by the `flecs.game` module, so before using it lets add this to the top of the script:
```js
using flecs.game
```
The implementation for the `Grid` component can be found [here](https://github.com/flecs-hub/flecs-game/blob/main/src/main.c). We only use it in the tutorial for demonstration purposes. In practice we could create many different components that handle layout for us.
The grid component lets us create multiple instances of an entity that are laid out in a 1, 2 or 3 dimensional grid. Let's apply this to the number of pillars, which we want to scale with the width of our fence. For this we need two things:
- The width of the fence
- The minimum spacing between pillars
We already have the fence width. Let's create another variable for the spacing:
```js
const pillar_spacing = 10
```
We can now calculate the number of pillars we want:
```js
const pillar_count = $width / $pillar_spacing
```
The `Grid` component can't consume the pillar code directly. Instead it accepts a prefab, so lets turn the existing pillars code into a prefab:
```js
Prefab Pillar {
- $color
- $pillar_box
}
```
Note how we left out `Position3`, as this will be set by the `Grid` component. Without further ado, let's create a row of pillars:
```js
pillars {
- Position3{y: $height/2}
- Grid{
x.count: $pillar_count
x.spacing: $pillar_spacing
prefab: Pillar
}
}
```
Let's disect what this code does:
- Create a `pillars` entity
- Create it at position `y: $height/2` so pillars don't sink into the ground
- With `$pillar_count` pillars on the x axis
- Spaced `$pillar_spacing` apart
- Using `Pillar` as prefab to instantiate the entities
Let's also change the code that instantiates our two fences to just one:
```
fence :- Fence{}
```
And increase the `width` of the fence to `60`:
```js
prop width : f32 = 60
```
We now get a number of pilars that matches the fence length:
[![a fence with multiple pillars](img/script_tutorial/tut_playground_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24pillar_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D%0A)
There's still something off though, which is that the pillars don't exactly line up with the ends up of the fence. We can fix this with a simple calculation that takes the number of pillars, and uses that to recompute the grid spacing.
```js
const grid_spacing = $width / ($pillar_count - 1)
```
Note that this takes into account that there is one more pillar than there are spaces between pillars. When we replace `pillar_spacing` in the grid with `grid_spacing`, we get the correct pillar alignment:
[![a fence with correctly aligned pillars](img/script_tutorial/tut_playground_pillar_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7B%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0A)
We can do the same thing for the bars. I'll skip right to the result since it involves very similar steps:
[![a fence with multiple pillars and bars](img/script_tutorial/tut_playground_grids.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D)
The values got tweaked a bit to get a more easthetically pleasing result when scaling up. Feel free to tune the variables to values that produce something you like!
We're almost done! There is only one thing left, which is to combine the fence assembly in a nested assembly so we can create an enclosing.
## Nested Assemblies
Now that we have our Fence assembly ready to go, we can build a higher level assemblies which reuses existing the `Fence` assembly. One typical thing we could do is instantate a `Fence` four times, so that it creates an enclosed space.
The code for this doesn't introduce anything beyond what we've already learned. Let's first setup a new assembly at the bottom of our script.
Inside the assembly, lets create `width` and `depth` properties, so we can define a rectangular area. Let's also add a `color` and `height` property that we can passthrough to our `Fence` assembly.
When put together, this is what it looks like:
```js
assembly Enclosing {
prop width: f32 = 40
prop height: f32 = 10
prop depth: f32 = 40
prop color: Rgb = {0.15, 0.1, 0.05}
// enclosing code goes here
}
```
Now we need to instantiate `Fence` four times, for each side of the rectangle. We'll also add a few convenience variables so we don't end
up writing the same divisions multiple times.
```js
const width_half = $width / 2
const depth_half = $depth / 2
const PI = 3.1415926
left {
- Position3{x: -$width_half}
- Rotation3{y: $PI/2}
- Fence{width: $depth, height:$, color:$}
}
right {
- Position3{x: $width_half}
- Rotation3{y: $PI/2}
- Fence{width: $depth, height:$, color:$}
}
back {
- Position3{z: -$depth_half}
- Fence{width: $width, height:$, color:$}
}
front {
- Position3{z: $depth_half}
- Fence{width: $width, height:$, color:$}
}
```
Note how the passthrough parameters are specified as `height:$`. This is a shorthand notation for `height: $height`, and can save a lot of typing when working with nested assemblies.
Let's now use our new `Enclosing` assembly by changing
```js
fence :- Fence{}
```
to
```
enclosing :- Enclosing{}
```
Here is what that looks like. You might need to zoom out a bit with the camera to see the full fence. Remember that you can do this by first clicking on the canvas to give it focus, then using the WASD keys to move the camera around. Don't forget to click on the canvas again to give focus back to the explorer.
[![an enclosing](img/script_tutorial/tut_playground_pasture.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A)
We can now easily modify our enclosing by passing in parameters for width and height:
```
enclosing :- Enclosing{width: 100, height: 30}
```
[![a tall enclosing](img/script_tutorial/tut_playground_pasture_2.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%7D%0A%0Apasture%20%3A-%20Enclosing%7B%0A%20%20width%3A%20100%2C%20%0A%20%20depth%3A%20100%0A%20%20height%3A%2030%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B52%2C%2041%2C%20-82%7D%0A%20%20-%20Rotation3%7B-0.45%2C%20-0.54%7D%0A%7D%0A)
Congratulations! You now know how to create assets in Flecs Script! If you'd like to see more examples of how assemblies can be used to create complex compositions of assets, take a look at the assemblies in the Flecs playground project:
https://github.com/flecs-hub/playground/tree/main/etc/assets
Additionally you can also try instantiating one of the assets that come preloaded in the playground. Try replacing the editor content with:
```js
using flecs.components.*
using assemblies
const PI = 3.1415926
plane {
- Position3{}
- Rotation3{$PI / 2}
- Rectangle{10000, 10000}
- Rgb{0.9, 0.9, 0.9}
}
town :- Town{}
```
[![a town](img/script_tutorial/tut_playground_town.png)](https://www.flecs.dev/explorer/?show=plecs,explorer_canvas&local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20assemblies%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atown%20%3A-%20Town%7B%7D)
That's all for this tutorial. Have fun creating, and don't hesitate to share the results in the `#showcase` channel on Discord! (link: https://discord.gg/caR2WmY)

View File

@@ -0,0 +1,638 @@
# JSON format
This document provides an overview of the JSON serializer format. For an overview of how to use the JSON serializer API, [see the JSON addon documentation](https://www.flecs.dev/flecs/group__c__addons__json.html).
## Value kinds
This section describes value kinds that are used by the JSON serializers.
### Name
A name field returns the name of an entity as returned by `ecs_get_name` or `flecs::entity::name`.
**Example**:
```json
"Earth"
```
### Path
A path field returns the path identifier of an entity as returned by `ecs_get_fullpath` or `flecs::entity::path`. The path contains the entity's name and the names of its parent and grandparents separated by a dot (`.`).
**Example**:
```json
"SolarSystem.Sun.Earth"
```
### Label
A label field returns the doc name of the entity as returned by `ecs_doc_get_name` or `flecs::entity::doc_name`. The doc name is a user-friendly name for the entity that does not need to be unique and may contain any character.
Serializing labels requires the [doc](https://www.flecs.dev/flecs/group__c__addons__doc.html) addon.
**Example**:
```json
"The Earth"
```
### Id
An id field returns a component id or pair. It is formatted as an array of at least one and at most three elements where the elements have the following meaning:
- Component _or_ First element of pair
- Second element of pair
- Type flag
Each element is formatted as a [Path](#path).
**Example**:
```json
["transform.Position"]
["Likes", "Apples"]
["Movement", 0, "SWITCH"]
```
### Id**
Same as [Id](#id) but with [Label](#label)'s instead of paths.
**Example**:
```json
["Entity position"]
["Located in", "San Francisco"]
```
### Value
A JSON object or scalar representing the value of a component. Component values can only be serialized if the component is described in the reflection framework (see the [meta addon](https://www.flecs.dev/flecs/group__c__addons__meta.html)).
When a component has no value (e.g. a tag) or is not described by the reflection framework, `0` will be used as placeholder.
**Example**:
```json
{"x": 10, "y": 20}
```
### Type**
A JSON object that represents the structure of a component type.
**Example**:
```json
{
"type_info": [{
"x": ["float"],
"y": ["float"]
}, 0, 0]
}
```
### Entity
The entity kind is an object that contains metadata and data of a single serialized entity, and is returned by the [Entity serializer](#entity-serializer).
The following sections describe the fields that an object of the entity kind may contain:
#### "path
The entity path.
Type: [Path](#path), optional
#### "label
The entity doc name.
Type: [Label](#label), optional
#### "brief
Brief description (as returned by `ecs_doc_get_brief`).
Type: string, optional
#### "link
Link to URL (as returned by `ecs_doc_get_link`).
Type: string, optional
#### "is_a
Base entities.
Type: Array([Entity](#entity)), optional
#### "ids
Component ids.
Type: Array([Id](#id))
#### "id_labels
Component labels.
Type: Array([Id label](#id-label)), optional
#### "hidden
Array indicating whether a component is hidden. Only applies to components of base entities (in the ["is_a"](is_a) array).
A base component is hidden when it is overridden by an entity higher in the inheritance hierarchy.
Type: Array(bool), optional
#### "values
Component values.
Type: Array([Value](#value)), optional
#### "type_info
Array with objects that describe the component types of the terms.
Type: Array([Type info](#type-info)), optional
#### Example
Default serializer options:
```json
{
"path":"Sun.Earth",
"ids":[
["Position"],
["flecs.core.ChildOf", "Sun"],
["flecs.core.Identifier", "flecs.core.Name"]
]
}
```
This example shows an entity with a base (an `IsA` relationship) with default serializer options:
```json
{
"path":"Sun.Earth",
"is_a": [{
"path":"planets.RockyPlanet",
"ids": [
["planets.HasRocks"]
]
}],
"ids":[
["Position"],
["flecs.core.ChildOf", "Sun"],
["flecs.core.Identifier", "flecs.core.Name"]
]
}
```
This example shows an entity with a base with all serializer options enabled:
```json
{
"path":"Sun.Earth",
"label": "The Earth",
"brief": "The planet you call home",
"link": "www.earth.com",
"is_a": [{
"path":"planets.RockyPlanet",
"label":"A rocky planet",
"ids": [
["Position"],
["planets.HasRocks"]
],
"id_labels": [
["Position"],
["HasRocks"]
],
"values":[0, 0],
"hidden":[true, false]
}],
"ids":[
["Position"],
["flecs.core.ChildOf", "Sun"],
["flecs.core.Identifier", "flecs.core.Name"]
],
"ids_labels":[
["Position"],
["ChildOf", "Sun"],
["Identifier", "Name"]
],
"values":[
{"x": 10, "y": 20},
0, 0
],
"type_info": [{
"x": ["float"],
"y": ["float"]
}, 0, 0]
}
```
### Result
The result kind is an object that contains the metadata and data of a single result returned by an iterator (see [Iterator](#iterator)).
The following sections describe the fields that an object of the entity kind may contain:
#### parent
Parent of the entity in current result.
Type: Array([Path](#path)), optional
#### entities
Array with paths of the returned entities.
Type: Array([Name](#name)), optional
#### entity_labels
Same as [entities](#entities), but with [Label](#label)'s instead of paths.
Type: Array([Label](#label)), optional
#### entity_ids
Same as [entities](#entities), but with numerical ids instead of paths.
Type: Array(Number), optional
#### sources
Array with paths of sources for each term. A subject indicates the actual entity on which a component is stored. If this is the matched entity (default) the array will contain a `0` element.
Type: Array([Path](#path)), optional
#### variables
Array with variable values for current result (see [query variables](Queries.md#variables)).
Type: Array([Path](#path)), optional
#### variable_labels
Same as [variables](#variables), but with [Label](#label)'s instead of paths.
Type: Array([Label](#label)), optional
#### variable_ids
Same as [variables](#variables), but with numerical ids instead of paths.
Type: Array(Number), optional
#### ids
Array with component ids. This list is more specific than the ids array provided by the top-level iterator object. The arrays can be different in the case of terms with an `Or` operator, or terms with wildcard ids.
Type: Array(string), optional
#### is_set
Array with booleans that can be used to determine whether terms with the `Optional` operator are set.
Type: Array(bool), optional
#### values
Array with component values. The array contains an element for each term. If a component has no value, or no value could be serialized for the component a `[]` element is added.
Each element in the array can be either an array with component values when the component is from the matched entity, or a single component value when the component is from another entity (like a parent, prefab, singleton).
Type: Array(Array([Value](#value))), optional
### Iterator
The iterator kind is an object that contains metadata and data of all the entities yielded by an iterator.
#### "ids
Array with ids for each term.
Type: Array(string), optional
#### "variables
Array with variable names (see [query variables](https://www.flecs.dev/flecs/md_docs_Queries.html/#variables)).
Type: Array(string), optional
#### "results
Array with elements for each returned result.
Type: Array([Result](#result))
#### "eval_duration
Time it took to serialize the iterator.
Type: Number
#### "type_info
Array with objects that describe the component types of the terms.
Type: Array([Type info](#type-info)), optional
#### Example
```json
{
"ids": ["Position", "Jedi"],
"results": [{
"entities": ["Luke", "Yoda"],
"values": [
[{ "x": 10, "y": 20 },
{ "x": 20, "y": 30 }],
0
]
}]
}
```
## Entity serializer
The entity serializer returns a JSON string of the [Entity](#entity) type. This format is returned by `ecs_entity_to_json` and `flecs::entity::to_json`.
### Serializer options
The following options (found in `ecs_entity_to_json_desc_t`) customize the output of the entity serializer:
#### serialize_path
Serialize the ["path"](#path-1) member.
**Default**: `true`
**Example**:
```json
{"path": "SolarSystem.Sun.Earth"}
```
#### serialize_label
Serialize the ["label"](#label-1) member.
**Default**: `false`
**Example**:
```json
{"label": "Rocky planet"}
```
#### serialize_brief
Serialize the ["brief"](#brief) member.
**Default**: `false`
**Example**:
```json
{"brief": "A rocky planet"}
```
#### serialize_link
Serialize the ["link"](#link) member.
**Default**: `false`
**Example**:
```json
{"brief": "www.rocky-planet.com"}
```
#### serialize_id_labels
Serializes the ["id_labels"](#id_labels) member.
**Default**: `false`
**Example**:
```json
{"id_labels": [["A Rocky Planet"], ["Has surface water"]]}
```
#### serialize_base
Serializes the ["is_a"](#is_a) member.
**Default**: `true`
**Example**:
```json
{"is_a": [{
"path":"planets.RockyPlanet",
"ids": [
["planets.HasRocks"]
]
}]}
```
#### serialize_private
Serialize private components. Private components are regular components with the `EcsPrivate` (or `flecs::Private`) tag. When `serialize_private` is false, private components will be hidden from all arrays.
**Example**:
```json
{"ids": ["Position", "PrivateComponent"]}
```
**Default**: `false`
#### serialize_hidden
Serializes the ["hidden"](#hidden) member.
**Example**:
```json
{"hidden":[true, false]}
```
Full example:
```json
{
"path":"Sun.Earth",
"is_a": [{
"path":"planets.RockyPlanet",
"ids": [
["Position"],
["planets.HasRocks"]
],
"hidden":[true, false]
}],
"ids":[
["Position"],
["flecs.core.ChildOf", "Sun"],
["flecs.core.Identifier", "flecs.core.Name"]
]
}
```
Note that `hidden[0]` is `true` in this example, because the `Position` component from `planets.RockyPlanet` is overridden by the entity.
**Default**: `false`
#### serialize_values
Serializes the ["values"](#values) member.
**Example**:
```json
{"values":[{"x":10, "y": 20}, 0]}
```
**Default**: `false`
#### serialize_type_info
Serialize the ["type_info"](#type_info) member.
**Example**:
```json
{
"type_info": [{
"x": ["float"],
"y": ["float"]
}, 0, 0]
}
```
## Iterator serializer
The entity serializer returns a JSON string of the [Iterator](#iterator) type. This format is returned by `ecs_iter_to_json`.
### Serializer options
#### serialize_term_ids
Serialize the top level ["ids"](#term_ids) member.
**Default**: `true`
**Example**:
Example result for query `Position, Jedi`
```json
{
"ids": ["Position", "Jedi"],
"results": [ ]
}
```
#### serialize_ids
Serialize the result specific ["ids"](#ids-1) member.
If the iterated query does not contain variable ids (either an `Or` term or a term with a wildcard id) the result specific `ids` member will exactly match the top-level `ids` member.
**Default**: `true`
**Example**:
Example result for query `Position, (Likes, *)`
```json
{
"ids": ["Position", "(Likes,*)"],
"results": [{
"ids": ["Position", "(Likes,Apples)"]
}]
}
```
#### serialize_sources
Serialize the ["sources"](#sources) member.
**Default**: `true`
**Example**:
Example result for query `Position, Position(parent)`
```json
{
"ids": ["Position", "Position"],
"results": [{
"entities": ["Parent.A", "Parent.B"],
"sources": [0, "Parent"]
}]
}
```
#### serialize_variables
Serialize the ["variables"](#variables) member.
**Default**: `true`
**Example**:
Example result for query `Position, (Likes, _Food)`
```json
{
"variables": ["Food"],
"results": [{
"ids": ["Position", "(Likes,Apples)"],
"variables": ["Apples"],
}]
}
```
#### serialize_is_set
Serialize the ["is_set"](#is_set) member.
**Default**: `true`
**Example**:
Example result for query `Position, ?Velocity`
```json
{
"ids": ["Position", "Velocity"],
"results": [{
"is_set": [true, false]
}]
}
```
#### serialize_values
Serialize the ["values"](#values) member.
**Default**: `true`
**Example**:
Example result for query `Position`
```json
{
"ids": ["Position"],
"results": [{
"values": [
[{
"x": 10,
"y": 20
}]
]
}]
}
```
#### serialize_entities
Serialize the ["entities"](#entities) member.
**Default**: `true`
**Example**:
```json
{
"results": [{
"entities": ["MyEntity"]
}]
}
```
#### serialize_entity_labels
Serialize the ["entity_labels"](#entity_labels) member.
**Default**: `false`
**Example**:
```json
{
"results": [{
"entities": ["Parent.MyEntity"],
"entity_labels": ["My entity"]
}]
}
```
#### serialize_variable_labels
Serialize the ["variable_labels"](#variable_labels) member.
**Default**: `false`
**Example**:
```json
{
"results": [{
"variables": ["GrannySmith"],
"variable_labels": ["Granny smith"]
}]
}
```
#### measure_eval_duration
Serialize the ["eval_duration"](#eval_duration) member.
**Default**: `false`
**Example**:
```json
{
"eval_duration": 0.001
}
```
#### serialize_type_info
Serialize the ["type_info"](#type_info_1) member.
**Default**: `false`
**Example**:
```json
{
"ids": ["Position", "Jedi"],
"type_info": [{
"x": ["float"],
"y": ["float"]
}, 0],
"results": [{
}]
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,918 @@
# Flecs Quickstart
This document provides a quick overview of the different features and concepts in Flecs with short examples. This is a good resource if you're just getting started or just want to get a better idea of what kind of features are available in Flecs!
## Building Flecs
To use Flecs, copy the [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) and [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) files from the repository root to your project's source folder. When building, make sure your build system is setup to do the following:
- If it is a C++ project, make sure to compile [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) as C code, for example by using `gcc`/`clang` instead of `g++`/`clang++`.
- If you are building on Windows and you're not using the Microsoft Visual Studio compiler, make sure to add `-lWs2_32` to **the end(!)** of the linker command. The socket API is used for connecting to Flecs explorer.
- When compiling Flecs with `gcc`/`clang`, add `-std=gnu99` to the compiler command. This ensures that addons that rely on time & socket functions are compiled correctly.
- C++ files that use Flecs must be compiled with `-std=c++0x` (C++11) or higher.
### Dynamic linking
To build Flecs as a dynamic library, remove this line from the top of the [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) file:
```c
#define flecs_STATIC
```
When compiling [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c), make sure to define `flecs_EXPORTS`, for example by adding `-Dflecs_EXPORTS` to the compiler command.
Alternatively Flecs can also be built as a dynamic library with the cmake, meson, bazel or [bake](https://github.com/SanderMertens/bake) build files provided in the repository. These use the files from `src` to build as opposed to the amalgamated files, which is better suited for development.
### Building with CMake
Locate `flecs` on your system (either by cloning or as a submodule) and use `add_subdirectory` or use `FetchContent` to download the source code from the master branch of the [flecs repository](https://github.com/SanderMertens/flecs). After that, add the following to your `CMakeLists.txt` file:
```cmake
target_link_libraries(${PROJECT_NAME} flecs::flecs_static)
```
### Building with Bake
Download or `git clone` the [flecs repository](https://github.com/SanderMertens/flecs) and run `bake` from inside the directory. After that, add the following to your `project.json` file's value property:
```json
"use": ["flecs"]
```
### Running tests (bake)
First make sure you have [bake](https://github.com/SanderMertens/bake) installed (see the bake repository for instructions).
Run the following commands to run all tests (use `-j` to specify the number of threads):
```bash
# Core test suite
bake run test/api -- -j 4
# Addon tests
bake run test/addons -- -j 4
# Reflection tests
bake run test/meta -- -j 4
# C++ tests
bake run test/cpp_api -- -j 4
```
To run tests with asan enabled, add `--cfg sanitize` to the command:
```bash
bake run --cfg sanitize test/api -- -j 4
```
#### Running tests (cmake, experimental)
First make sure to clone [bake](https://github.com/SanderMertens/bake).
Run the following commands to run all the tests:
```bash
# Generate make files for Flecs and tests
cmake -DFLECS_TESTS=ON -DBAKE_DIRECTORY="path to cloned bake repository"
# Build flecs and test suites
cmake --build . -j 4
# Run the tests
ctest -C Debug --verbose
```
### Emscripten
When building for emscripten, add the following command line options to the `emcc` link command:
```bash
-s ALLOW_MEMORY_GROWTH=1
-s STACK_SIZE=1mb
-s EXPORTED_RUNTIME_METHODS=cwrap
-s MODULARIZE=1
-s EXPORT_NAME="my_app"
```
### Addons
Flecs has a modular architecture that makes it easy to only build the features you really need. By default all addons are built. To customize a build, first define `FLECS_CUSTOM_BUILD`, then add defines for the addons you need. For example:
```c
#define FLECS_CUSTOM_BUILD // Don't build all addons
#define FLECS_SYSTEM // Build FLECS_SYSTEM
```
Additionally, you can also specify addons to exclude from a build by adding `NO` to the define:
```c
#define FLECS_NO_LOG
```
The following addons can be configured:
Addon | Description | Define |
--------------|--------------------------------------------------|---------------------|
[Cpp](/flecs/group__cpp.html) | C++11 API | FLECS_CPP |
[Module](/flecs/group__c__addons__module.html) | Organize game logic into reusable modules | FLECS_MODULE |
[System](flecs/group__c__addons__system.html) | Create & run systems | FLECS_SYSTEM |
[Pipeline](/flecs/group__c__addons__pipeline.html) | Automatically schedule & multithread systems | FLECS_PIPELINE |
[Timer](/flecs/group__c__addons__timer.html) | Run systems at time intervals or at a rate | FLECS_TIMER |
[Meta](/flecs/group__c__addons__meta.html) | Flecs reflection system | FLECS_META |
[Meta_C](/flecs/group__c__addons__meta__c.html) | (C) Utilities for auto-inserting reflection data | FLECS_META_C |
[Units](/flecs/group__c__addons__units.html) | Builtin unit types | FLECS_UNITS |
[Expr](/flecs/group__c__addons__expr.html) | String format optimized for ECS data | FLECS_EXPR |
[JSON](/flecs/group__c__addons__json.html) | JSON format | FLECS_JSON |
[Doc](/flecs/group__c__addons__doc.html) | Add documentation to components, systems & more | FLECS_DOC |
[Coredoc](/flecs/group__c__addons__coredoc.html) | Documentation for builtin components & modules | FLECS_COREDOC |
[Http](/flecs/group__c__addons__http.html) | Tiny HTTP server for processing simple requests | FLECS_HTTP |
[Rest](/flecs/group__c__addons__rest.html) | REST API for showing entities in the browser | FLECS_REST |
[Parser](/flecs/group__c__addons__parser.html) | Create entities & queries from strings | FLECS_PARSER |
[Plecs](/flecs/group__c__addons__plecs.html) | Small utility language for asset/scene loading | FLECS_PLECS |
[Rules](/flecs/group__c__addons__rules.html) | Powerful prolog-like query language | FLECS_RULES |
[Snapshot](/flecs/group__c__addons__snapshot.html) | Take snapshots of the world & restore them | FLECS_SNAPSHOT |
[Stats](/flecs/group__c__addons__stats.html) | Functions for collecting statistics | FLECS_STATS |
[Monitor](/flecs/group__c__addons__monitor.html) | Periodically collect & store flecs statistics | FLECS_MONITOR |
[Metrics](/flecs/group__c__addons__metrics.html) | Create metrics from user-defined components | FLECS_METRICS |
[Alerts](/flecs/group__c__addons__alerts.html) | Create alerts from user-defined queries | FLECS_ALERTS |
[Log](/flecs/group__c__addons__log.html) | Extended tracing and error logging | FLECS_LOG |
[Journal](/flecs/group__c__addons__journal.html) | Journaling of API functions | FLECS_JOURNAL |
[App](/flecs/group__c__addons__app.html) | Flecs application framework | FLECS_APP |
[OS API Impl](/flecs/group__c__addons__os__api__impl.html) | Default OS API implementation for Posix/Win32 | FLECS_OS_API_IMPL |
## Concepts
This section contains an overview of all the different concepts in Flecs and how they wire together. The sections in the quickstart go over them in more detail and with code examples.
![Flecs Overview](img/flecs-quickstart-overview.png)
### World
The world is the container for all ECS data. It stores the entities and their components, does queries and runs systems. Typically there is only a single world, but there is no limit on the number of worlds an application can create.
```c
ecs_world_t *world = ecs_init();
// Do the ECS stuff
ecs_fini(world);
```
```cpp
flecs::world world;
// Do the ECS stuff
```
### Entity
An entity is a unique thing in the world, and is represented by a 64 bit id. Entities can be created and deleted. If an entity is deleted it is no longer considered "alive". A world can contain up to 4 billion(!) alive entities. Entity identifiers contain a few bits that make it possible to check whether an entity is alive or not.
```c
ecs_entity_t e = ecs_new_id(world);
ecs_is_alive(world, e); // true!
ecs_delete(world, e);
ecs_is_alive(world, e); // false!
```
```cpp
auto e = world.entity();
e.is_alive(); // true!
e.destruct();
e.is_alive(); // false!
```
Entities can have names which makes it easier to identify them in an application. In C++ the name can be passed to the constructor. In C a name can be assigned with the `ecs_entity_init` function/`ecs_entity` macro. If a name is provided during entity creation time and an entity with that name already exists, the existing entity will be returned.
```c
ecs_entity_t e = ecs_entity(world, { .name = "Bob" });
printf("Entity name: %s\n", ecs_get_name(world, e));
```
```cpp
auto e = world.entity("Bob");
std::cout << "Entity name: " << e.name() << std::endl;
```
Entities can be looked up by name with the `lookup` function:
```c
ecs_entity_t e = ecs_lookup(world, "Bob");
```
```cpp
auto e = world.lookup("Bob");
```
### Id
An id is a 64 bit number that can encode anything that can be added to an entity. In flecs this can be either a component, tag or a pair. A component is data that can be added to an entity. A tag is an "empty" component. A pair is a combination of two component/tag ids which is used to encode entity relationships. All entity/component/tag identifiers are valid ids, but not all ids are valid entity identifier.
The following sections describe components, tags and pairs in more detail.
### Component
A component is a type of which instances can be added and removed to entities. Each component can be added only once to an entity (though not really, see [Pair](#pair)). In C applications components must be registered before use. In C++ this happens automatically.
```c
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t e = ecs_new_id(world);
// Add a component. This creates the component in the ECS storage, but does not
// assign it with a value.
ecs_add(world, e, Velocity);
// Set the value for the Position & Velocity components. A component will be
// added if the entity doesn't have it yet.
ecs_set(world, e, Position, {10, 20});
ecs_set(world, e, Velocity, {1, 2});
// Get a component
const Position *p = ecs_get(world, e, Position);
// Remove component
ecs_remove(world, e, Position);
```
```cpp
auto e = world.entity();
// Add a component. This creates the component in the ECS storage, but does not
// assign it with a value.
e.add<Velocity>();
// Set the value for the Position & Velocity components. A component will be
// added if the entity doesn't have it yet.
e.set<Position>({10, 20})
.set<Velocity>({1, 2});
// Get a component
const Position *p = e.get<Position>();
// Remove component
e.remove<Position>();
```
Each component is associated by a unique entity identifier by Flecs. This makes it possible to inspect component data, or attach your own data to components. C applications can use the `ecs_id` macro to get the entity id for a component. C++ applications can use the `world::id` function:
```c
ECS_COMPONENT(world, Position);
ecs_entity_t pos_e = ecs_id(Position);
printf("Name: %s\n", ecs_get_name(world, pos_e)); // outputs 'Name: Position'
// It's possible to add components like you would for any entity
ecs_add(world, pos_e, Serializable);
```
```cpp
flecs::entity pos_e = world.entity<Position>();
std::cout << "Name: " << pos_e.name() << std::endl; // outputs 'Name: Position'
// It's possible to add components like you would for any entity
pos_e.add<Serializable>();
```
The thing that makes an ordinary entity a component is the `EcsComponent` (or `flecs::Component`, in C++) component. This is a builtin component that tells Flecs how much space is needed to store a component, and can be inspected by applications:
```c
ECS_COMPONENT(world, Position);
ecs_entity_t pos_e = ecs_id(Position);
const EcsComponent *c = ecs_get(world, pos_e, EcsComponent);
printf("Component size: %u\n", c->size);
```
```cpp
flecs::entity pos_e = world.entity<Position>();
const EcsComponent *c = pos_e.get<flecs::Component>();
std::cout << "Component size: " << c->size << std::endl;
```
Because components are stored as regular entities, they can in theory also be deleted. To prevent unexpected accidents however, by default components are registered with a tag that prevents them from being deleted. If this tag were to be removed, deleting a component would cause it to be removed from all entities. For more information on these policies, see [Relationship cleanup properties](Relationships.md#cleanup-properties).
### Tag
A tag is a component that does not have any data. In Flecs tags can be either empty types (in C++) or regular entities (C & C++) that do not have the `EcsComponent` component (or have an `EcsComponent` component with size 0). Tags can be added & removed using the same APIs as adding & removing components, but because tags have no data, they cannot be assigned a value. Because tags (like components) are regular entities, they can be created & deleted at runtime.
```c
// Create Enemy tag
ecs_entity_t Enemy = ecs_new_id(world);
// Create entity, add Enemy tag
ecs_entity_t e = ecs_new_id(world);
ecs_add_id(world, e, Enemy);
ecs_has_id(world, e, Enemy); // true!
ecs_remove_id(world, e, Enemy);
ecs_has_id(world, e, Enemy); // false!
```
```cpp
// Option 1: create Tag as empty struct
struct Enemy { };
// Create entity, add Enemy tag
auto e = world.entity().add<Enemy>();
e.has<Enemy>(); // true!
e.remove<Enemy>();
e.has<Enemy>(); // false!
// Option 2: create Tag as entity
auto Enemy = world.entity();
// Create entity, add Enemy tag
auto e = world.entity().add(Enemy);
e.has(Enemy); // true!
e.remove(Enemy);
e.has(Enemy); // false!
```
Note that both options in the C++ example achieve the same effect. The only difference is that in option 1 the tag is fixed at compile time, whereas in option 2 the tag can be created dynamically at runtime.
When a tag is deleted, the same rules apply as for components (see [Relationship cleanup properties](Relationships.md#cleanup-properties)).
### Pair
A pair is a combination of two entity ids. Pairs can be used to store entity relationships, where the first id represents the relationship kind and the second id represents the relationship target (called "object"). This is best explained by an example:
```c
// Create Likes relationship
ecs_entity_t Likes = ecs_new_id(world);
// Create a small graph with two entities that like each other
ecs_entity_t Bob = ecs_new_id(world);
ecs_entity_t Alice = ecs_new_id(world);
ecs_add_pair(world, Bob, Likes, Alice); // Bob likes Alice
ecs_add_pair(world, Alice, Likes, Bob); // Alice likes Bob
ecs_has_pair(world, Bob, Likes, Alice); // true!
ecs_remove_pair(world, Bob, Likes, Alice);
ecs_has_pair(world, Bob, Likes, Alice); // false!
```
```cpp
// Create Likes relationship as empty type (tag)
struct Likes { };
// Create a small graph with two entities that like each other
auto Bob = world.entity();
auto Alice = world.entity();
Bob.add<Likes>(Alice); // Bob likes Alice
Alice.add<Likes>(Bob); // Alice likes Bob
Bob.has<Likes>(Alice); // true!
Bob.remove<Likes>(Alice);
Bob.has<Likes>(Alice); // false!
```
A pair can be encoded in a single 64 bit identifier by using the `ecs_pair` macro in C, or the `world.pair` function in C++:
```c
ecs_id_t id = ecs_pair(Likes, Bob);
```
```cpp
flecs::id id = world.pair<Likes>(Bob);
```
The following examples show how to get back the elements from a pair:
```c
if (ecs_id_is_pair(id)) {
ecs_entity_t relationship = ecs_pair_first(world, id);
ecs_entity_t target = ecs_pair_second(world, id);
}
```
```cpp
if (id.is_pair()) {
auto relationship = id.first();
auto target = id.second();
}
```
A component or tag can be added multiple times to the same entity as long as it is part of a pair, and the pair itself is unique:
```c
ecs_add_pair(world, Bob, Eats, Apples);
ecs_add_pair(world, Bob, Eats, Pears);
ecs_add_pair(world, Bob, Grows, Pears);
ecs_has_pair(world, Bob, Eats, Apples); // true!
ecs_has_pair(world, Bob, Eats, Pears); // true!
ecs_has_pair(world, Bob, Grows, Pears); // true!
```
```cpp
Bob.add(Eats, Apples);
Bob.add(Eats, Pears);
Bob.add(Grows, Pears);
Bob.has(Eats, Apples); // true!
Bob.has(Eats, Pears); // true!
Bob.has(Grows, Pears); // true!
```
The `target` function can be used in C and C++ to get the object for a relationship:
```c
ecs_entity_t o = ecs_get_target(world, Alice, Likes, 0); // Returns Bob
```
```cpp
auto o = Alice.target<Likes>(); // Returns Bob
```
Entity relationships enable lots of interesting patterns and possibilities. Make sure to check out the [Relationships manual](Relationships.md).
### Hierarchies
Flecs has builtin support for hierarchies with the builtin `EcsChildOf` (or `flecs::ChildOf`, in C++) relationship. A hierarchy can be created with the regular relationship API, or with the `child_of` shortcut in C++:
```c
ecs_entity_t parent = ecs_new_id(world);
// ecs_new_w_pair is the same as ecs_new_id + ecs_add_pair
ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent);
// Deleting the parent also deletes its children
ecs_delete(world, parent);
```
```cpp
auto parent = world.entity();
auto child = world.entity().child_of(parent);
// Deleting the parent also deletes its children
parent.destruct();
```
When entities have names, they can be used together with hierarchies to generate path names or do relative lookups:
```c
ecs_entity_t parent = ecs_entity(world, {
.name = "parent"
});
ecs_entity_t child = ecs_entity(world, {
.name = "child"
});
ecs_add_pair(world, child, EcsChildOf, parent);
char *path = ecs_get_fullpath(world, child);
printf("%s\n", path); // output: 'parent.child'
ecs_os_free(path);
ecs_lookup_path(world, 0, "parent.child"); // returns child
ecs_lookup_path(world, parent, "child"); // returns child
```
```cpp
auto parent = world.entity("parent");
auto child = world.entity("child").child_of(parent);
std::cout << child.path() << std::endl; // output: 'parent::child'
world.lookup("parent::child"); // returns child
parent.lookup("child"); // returns child
```
Queries (see below) can use hierarchies to order data breadth-first, which can come in handy when you're implementing a transform system:
```c
ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){
.filter.terms = {
{ ecs_id(Position) },
{ ecs_id(Position), .src = {
.flags = EcsCascade, // Breadth-first order
.trav = EcsChildOf // Use ChildOf relationship for traversal
}}
}
});
ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
Position *p = ecs_field(&it, Position, 1);
Position *p_parent = ecs_field(&it, Position, 2);
for (int i = 0; i < it.count; i++) {
// Do the thing
}
}
```
```cpp
auto q = world.query_builder<Position, Position>()
.term_at(2).parent().cascade()
.build();
q.each([](Position& p, Position& p_parent) {
// Do the thing
});
```
### Instancing
Flecs has builtin support for instancing (sharing a single component with multiple entities) through the builtin `EcsIsA` relationship (`flecs::IsA` in C++). An entity with an `IsA` relationship to a base entity "inherits" all entities from that base:
```c
// Shortcut to create entity & set a component
ecs_entity_t base = ecs_set(world, 0, Triangle, {{0, 0}, {1, 1}, {-1, -1}});
// Create entity that shares components with base
ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base);
const Triangle *t = ecs_get(world, e, Triangle); // gets Triangle from base
```
```cpp
auto base = world.entity().set<Triangle>({{0, 0}, {1, 1}, {-1, -1}});
// Create entity that shares components with base
auto e = world.entity().is_a(base);
const Triangle *t = e.get<Triangle>(); // gets Triangle from base
```
Entities can override components from their base:
```c
// Add private instance of Triangle to e, copy value from base
ecs_add(world, e, Triangle);
```
```cpp
// Add private instance of Triangle to e, copy value from base
e.add<Triangle>();
```
Instancing can be used to build modular prefab hierarchies, as the foundation of a batched renderer with instancing support, or just to reduce memory footprint by sharing common data across entities.
### Type
The type (often referred to as "archetype") is the list of ids an entity has. Types can be used for introspection which is useful when debugging, or when for example building an entity editor. The most common thing to do with a type is to convert it to text and print it:
```c
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ecs_entity_t e = ecs_new_id(world);
ecs_add(world, e, Position);
ecs_add(world, e, Velocity);
const ecs_type_t *type = ecs_get_type(world, e);
char *type_str = ecs_type_str(world, type);
printf("Type: %s\n", type_str); // output: 'Position,Velocity'
ecs_os_free(type_str);
```
```cpp
auto e = ecs.entity()
.add<Position>()
.add<Velocity>();
std::cout << e.type().str() << std::endl; // output: 'Position,Velocity'
```
A type can also be iterated by an application:
```c
const ecs_type_t *type = ecs_get_type(world, e);
for (int i = 0; i < type->count; i++) {
if (type->array[i] == ecs_id(Position)) {
// Found Position component!
}
}
```
```cpp
e.each([&](flecs::id id) {
if (id == world.id<Position>()) {
// Found Position component!
}
});
```
### Singleton
A singleton is a single instance of a component that can be retrieved without an entity. The functions for singletons are very similar to the regular API:
```c
// Set singleton component
ecs_singleton_set(world, Gravity, { 9.81 });
// Get singleton component
const Gravity *g = ecs_singleton_get(world, Gravity);
```
```cpp
// Set singleton component
world.set<Gravity>({ 9.81 });
// Get singleton component
const Gravity *g = world.get<Gravity>();
```
Singleton components are created by adding the component to its own entity id. The above code examples are shortcuts for these regular API calls:
```c
ecs_set(world, ecs_id(Gravity), Gravity, {10, 20});
const Gravity *g = ecs_get(world, ecs_id(Gravity), Gravity);
```
```cpp
flecs::entity grav_e = world.entity<Gravity>();
grav_e.set<Gravity>({10, 20});
const Gravity *g = grav_e.get<Gravity>();
```
The following examples show how to query for a singleton component:
```c
// Create query that matches Gravity as singleton
ecs_query_t *q = ecs_query(ecs, {
.filter.terms = {
// Regular component
{ .id = ecs_id(Velocity) },
// A singleton is a component matched on itself
{ .id = ecs_id(Gravity), .src.id = ecs_id(Gravity) }
}
});
// Create a system using the query DSL with a singleton:
ECS_SYSTEM(world, ApplyGravity, EcsOnUpdate, Velocity, Gravity($));
```
```cpp
world.query_builder<Velocity, Gravity>()
.term_at(2).singleton()
.build();
```
### Filter
Filters are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter:
```c
// Initialize a filter with 2 terms on the stack
ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){
.terms = {
{ ecs_id(Position) },
{ ecs_pair(EcsChildOf, parent) }
}
});
// Iterate the filter results. Because entities are grouped by their type there
// are two loops: an outer loop for the type, and an inner loop for the entities
// for that type.
ecs_iter_t it = ecs_filter_iter(world, f);
while (ecs_filter_next(&it)) {
// Each type has its own set of component arrays
Position *p = ecs_field(&it, Position, 1);
// Iterate all entities for the type
for (int i = 0; i < it.count; i++) {
printf("%s: {%f, %f}\n", ecs_get_name(world, it.entities[i]),
p[i].x, p[i].y);
}
}
ecs_filter_fini(f);
```
```cpp
// For simple queries the each function can be used
world.each([](Position& p, Velocity& v) { // flecs::entity argument is optional
p.x += v.x;
p.y += v.y;
});
// More complex filters can first be created, then iterated
auto f = world.filter_builder<Position>()
.term(flecs::ChildOf, parent)
.build();
// Option 1: each() function that iterates each entity
f.each([](flecs::entity e, Position& p) {
std::cout << e.name() << ": {" << p.x << ", " << p.y << "}" << std::endl;
});
// Option 2: iter() function that iterates each archetype
f.iter([](flecs::iter& it, Position *p) {
for (int i : it) {
std::cout << it.entity(i).name()
<< ": {" << p[i].x << ", " << p[i].y << "}" << std::endl;
}
});
```
Filters can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs.
The following example shows a filter that matches all entities with a parent that do not have `Position`:
```c
ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){
.terms = {
{ ecs_pair(EcsChildOf, EcsWildcard) }
{ ecs_id(Position), .oper = EcsNot },
}
});
// Iteration code is the same
```
```cpp
auto f = world.filter_builder<>()
.term(flecs::ChildOf, flecs::Wildcard)
.term<Position>().oper(flecs::Not)
.build();
// Iteration code is the same
```
### Query
Queries are cached versions of filters. They are slower to create than filters, but much faster to iterate since this just means iterating their cache.
The API for queries is similar to filters:
```c
// Create a query with 2 terms
ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){
.filter.terms = {
{ ecs_id(Position) },
{ ecs_pair(EcsChildOf, EcsWildcard) }
}
});
ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
// Same as for filters
}
```
```cpp
// Create a query with two terms
auto q = world.query_builder<Position>()
.term(flecs::ChildOf, flecs::Wildcard)
.build();
// Iteration is the same as filters
```
When using queries, make sure to reuse a query object instead of creating a new one each time you need it. Query creation is expensive, and many of the performance benefits of queries are lost when they are created in loops.
See the [query manual](Queries.md) for more details.
### System
A system is a query combined with a callback. Systems can be either ran manually or ran as part of an ECS-managed main loop (see [Pipeline](#pipeline)). The system API looks similar to queries:
```c
// Option 1, use the ECS_SYSTEM convenience macro
ECS_SYSTEM(world, Move, 0, Position, Velocity);
ecs_run(world, Move, delta_time, NULL); // Run system
// Option 2, use the ecs_system_init function/ecs_system macro
ecs_entity_t move_sys = ecs_system(world, {
.query.filter.terms = {
{ecs_id(Position)},
{ecs_id(Velocity)},
},
.callback = Move
});
ecs_run(world, move_sys, delta_time, NULL); // Run system
// The callback code (same for both options)
void Move(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
p[i].x += v[i].x * it->delta_time;
p[i].y += v[i].y * it->delta_time;
}
}
```
```cpp
// Use each() function that iterates each individual entity
auto move_sys = world.system<Position, Velocity>()
.iter([](flecs::iter it, Position *p, Velocity *v) {
for (int i : it) {
p[i].x += v[i].x * it.delta_time();
p[i].y += v[i].y * it.delta_time();
}
});
// Just like with filters & queries, systems have both the iter() and
// each() methods to iterate entities.
move_sys.run();
```
Systems are stored as entities with an `EcsSystem` component (`flecs::System` in C++), similar to components. That means that an application can use a system as a regular entity:
```c
printf("System: %s\n", ecs_get_name(world, move_sys));
ecs_add(world, move_sys, EcsOnUpdate);
ecs_delete(world, move_sys);
```
```cpp
std::cout << "System: " << move_sys.name() << std::endl;
move_sys.add(flecs::OnUpdate);
move_sys.destruct();
```
### Pipeline
A pipeline is a list of tags that when matched, produces a list of systems to run. These tags are also referred to as a system "phase". Flecs comes with a default pipeline that has the following phases:
```c
EcsOnLoad
EcsPostLoad
EcsPreUpdate
EcsOnUpdate
EcsOnValidate
EcsPostUpdate
EcsPreStore
EcsOnStore
```
```cpp
flecs::OnLoad
flecs::PostLoad
flecs::PreUpdate
flecs::OnUpdate
flecs::OnValidate
flecs::PostUpdate
flecs::PreStore
flecs::OnStore
```
When a pipeline is executed, systems are ran in the order of the phases. This makes pipelines and phases the primary mechanism for defining ordering between systems. The following code shows how to assign systems to a pipeline, and how to run the pipeline with the `progress()` function:
```c
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity);
ECS_SYSTEM(world, Transform, EcsPostUpdate, Position, Transform);
ECS_SYSTEM(world, Render, EcsOnStore, Transform, Mesh);
ecs_progress(world, 0); // run systems in default pipeline
```
```cpp
world.system<Position, Velocity>("Move").kind(flecs::OnUpdate).each( ... );
world.system<Position, Transform>("Transform").kind(flecs::PostUpdate).each( ... );
world.system<Transform, Mesh>("Render").kind(flecs::OnStore).each( ... );
world.progress();
```
Because phases are just tags that are added to systems, applications can use the regular API to add/remove systems to a phase:
```c
ecs_remove_id(world, Move, EcsOnUpdate);
ecs_add_id(world, Move, EcsPostUpdate);
```
```cpp
move_sys.add(flecs::OnUpdate);
move_sys.remove(flecs::PostUpdate);
```
Inside a phase, systems are guaranteed to be ran in their declaration order.
### Observer
Observers are callbacks that are invoked when one or more events matches the query of an observer. Events can be either user defined or builtin. Examples of builtin events are `OnAdd`, `OnRemove` and `OnSet`.
When an observer has a query with more than one component, the observer will not be invoked until the entity for which the event is emitted satisfies the entire query.
An example of an observer with two components:
```c
ecs_observer(world, {
.filter.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }},
.event = EcsOnSet,
.callback = OnSetPosition
});
// Callback code is same as system
ecs_entity_t e = ecs_new_id(world); // Doesn't invoke the observer
ecs_set(world, e, Position, {10, 20}); // Doesn't invoke the observer
ecs_set(world, e, Velocity, {1, 2}); // Invokes the observer
ecs_set(world, e, Position, {20, 40}); // Invokes the observer
```
```cpp
world.observer<Position, Velocity>("OnSetPosition").event(flecs::OnSet).each( ... );
auto e = ecs.entity(); // Doesn't invoke the observer
e.set<Position>({10, 20}); // Doesn't invoke the observer
e.set<Velocity>({1, 2}); // Invokes the observer
e.set<Position>({20, 30}); // Invokes the observer
```
### Module
A module is a function that imports and organizes components, systems, triggers, observers, prefabs into the world as reusable units of code. A well designed module has no code that directly relies on code of another module, except for components definitions. All module contents are stored as child entities inside the module scope with the `ChildOf` relationship. The following examples show how to define a module in C and C++:
```c
// Module header (e.g. MyModule.h)
typedef struct {
float x;
float y;
} Position;
extern ECS_COMPONENT_DECLARE(Position);
// The import function name has to follow the convention: <ModuleName>Import
void MyModuleImport(ecs_world_t *world);
// Module source (e.g. MyModule.c)
ECS_COMPONENT_DECLARE(Position);
void MyModuleImport(ecs_world_t *world) {
ECS_MODULE(world, MyModule);
ECS_COMPONENT_DEFINE(world, Position);
}
// Import code
ECS_IMPORT(world, MyModule);
```
```cpp
struct my_module {
my_module(flecs::world& world) {
world.module<my_module>();
// Define components, systems, triggers, ... as usual. They will be
// automatically created inside the scope of the module.
}
};
// Import code
world.import<my_module>();
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,355 @@
# REST API
This document provides an overview of the REST API. The Flecs REST API enables (web) clients to inspect the contents of the ECS store, by remotely running queries and requesting entities.
## Enable the REST API
The REST API can be enabled in an application by instantiating the `EcsRest`/`flecs::Rest` component:
```c
// Start REST API with default parameters
ecs_singleton_set(world, EcsRest, {0});
```
```cpp
// Start REST API with default parameters
world.set<flecs::Rest>({});
```
When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the REST interface can be enabled like this:
```c
// Start application main loop, enable REST interface
ecs_app_run(world, &(ecs_app_desc_t){
.enable_rest = true
});
```
```cpp
// Start application main loop, enable REST interface
world.app()
.enable_rest()
.run();
```
To test if the REST API is working, navigate to http://localhost:27750/entity/flecs/core/World. Upon success this request should return a reply that looks like:
```json
{"path":"World", "ids":[["flecs.rest.Rest"], ["flecs.core.Identifier", "flecs.core.Name"], ["flecs.core.Identifier", "flecs.core.Symbol"], ["flecs.core.ChildOf", "flecs.core"], ["flecs.doc.Description", "flecs.core.Name"], ["flecs.doc.Description", "flecs.doc.Brief"]]}
```
When the monitor module is imported, the REST API provides a `stats` endpoint with statistics for different time intervals:
```c
// Import monitor addon
ECS_IMPORT(world, FlecsMonitor);
```
```cpp
world.import<flecs::monitor>();
```
When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the monitoring can be enabled like this:
```c
// Start application main loop, enable REST interface and monitoring
ecs_app_run(world, &(ecs_app_desc_t){
.enable_rest = true,
.enable_monitor = true
});
```
```cpp
// Start application main loop, enable REST interface and monitoring
world.app()
.enable_rest()
.enable_monitor()
.run();
```
For the full C/C++ API reference [see the REST addon documentation](https://www.flecs.dev/flecs/group__c__addons__rest.html).
## Explorer
An application with REST enabled can be remotely monitored with the [Flecs Explorer](https://github.com/flecs-hub/explorer). When the application is running with REST enabled, navigate to https://flecs.dev/explorer. This should connect the Explorer to the application.
When the connection is successful, the Explorer should look similar to this:
![Remote Explorer](img/explorer-remote.png)
The remote icon next to the title should be visible. If the connection is not successful it could be that the explorer did not receive a response fast enough. To force the explorer to connect remotely, add `?remote=true` to the request: https://flecs.dev/explorer?remote=true.
If connection issues persist, the browser could be preventing connections to a local application. See the [Explorer repository README](https://github.com/flecs-hub/explorer) for more information on how to workaround this and other issues.
**Tip**: To show the application title in the explorer, pass the command line arguments to `ecs_init`/`flecs::world::world`:
```c
ecs_world_t *world = ecs_init_w_args(argc, argv);
```
```c++
flecs::world world(argc, argc);
```
### Queries
The search bar in the explorer makes it possible to directly query the ECS storage. A query can be entered in the "Search" box, and needs to be specified in the query DSL (see [query manual](Queries.md#query-dsl)). An example:
![Remote Explorer](img/explorer-query.png)
The default query engine used by the search bar is the rules engine, which means it is possible to use features like query variables:
![Remote Explorer](img/explorer-rules.png)
It is also possible to inspect the results of existing queries. This can be done by entering a `?-`, followed by the name of the query. Queries can be given a name by setting the `.entity` field to a named entity in C:
```c
ecs_query_t *q = ecs_query(world, {
.entity = ecs_entity(world, { .name = "Move" }),
.filter.terms = {
{ ecs_id(Position) },
{ ecs_id(Velocity) },
}
});
```
In C++ a name can be provided to the query factory method:
```cpp
auto q = world.query<Position, Velocity>("Move");
```
This also works for filters and rules. Because systems are queries, the name of a system can be entered to inspect the entities matched by the system:
![Remote Explorer](img/explorer-system.png)
When a named rule query has variables, variables can be optionally provided as arguments to the query. The following example provides the value `Apples` to the query variable `food` for query `eats_query`, which constrains the results to only show results with `Apples`:
![Remote Explorer](img/explorer-arguments.png)
The underlying query that was used for this screenshot was created like this:
```c
ecs_rule(world, {
.entity = ecs_entity(world, { .name = "eats_query" }),
.expr = "(Eats, $food)"
});
```
## Endpoints
This section describes the endpoints of the REST API.
### entity
```
GET /entity/<path>
```
The entity endpoint requests data from an entity. The path is the entity path or name of the entity to query for. The reply is formatted according to the [JSON serializer Entity](JsonFormat.md#entity) type.
The following parameters can be provided to the endpoint:
#### path
Add path (name) for entity.
**Default**: true
#### label
Add label (from doc framework) for entity.
**Default**: false
#### brief
Add brief description (from doc framework) for entity.
**Default**: false
#### link
Add link (from doc framework) for entity.
**Default**: false
#### id_labels
Add labels (from doc framework) for (component) ids.
**Default**: false
#### base
Add base components.
**Default**: true
#### values
Add component values.
**Default**: false
#### private
Add private components.
**Default**: false
#### type_info
Add reflection data for component types.
**Default**: false
#### alerts
Add active alerts for entity.
**Default**: false
#### Example
```
/entity/my_entity
/entity/my_entity?values=true
/entity/parent/child
```
### enable
```
PUT /enable/<path>
```
The enable endpoint enables the entity specified by the path by removing the `flecs.core.Disabled` component. If the entity does not have the `flecs.core.Disabled` component, the endpoint has no side effects.
### disable
```
PUT /disable/<path>
```
The disable endpoint disables the entity specified by the path by adding the `flecs.core.Disabled` component. If the entity already has the `flecs.core.Disabled` component, the endpoint has no side effects.
### delete
```
PUT /delete/<path>
```
The delete endpoint deletes the entity specified by the path.
### set
```
PUT /set/<path>&data={...}
```
The set endpoint sets components for the entity specified by the path. The data argument uses the same layout as the `ids` and `values` fields produced by the `ecs_entity_to_json` function. An example:
```json
{
"ids": [["Position"], ["Velocity"], ["Eats", "Apples"]],
"values": [
{"x": 10, "y": 20},
{"x": 1, "y": 1},
{"amount": 3}
]
}
```
### query
```
GET /query?q=<query>
```
The query endpoint requests data for a query. The implementation uses the
rules query engine. The reply is formatted as an [JSON serializer Iterator](JsonFormat.md#iterator) type.
The following parameters can be provided to the endpoint:
#### name
Specify the name of an existing query or system.
#### offset
Skip the first _offset_ number of entities.
**Default**: 0
#### limit
Return at most _limit_ number of entities.
**Default**: 100
#### term_ids
Add top-level "ids" array with components as specified by query.
**Default**: false
#### term_labels
Add top-level "id_labels" array with doc names of components as specified by query.
**Default**: false
#### ids
Add result-specific "ids" array with components as matched. Can be different from top-level "ids" array for queries with wildcards.
**Default**: false
#### id_labels
Add result-specific "id_labels" array with doc names of components as matched. Can be different from top-level "term_labels" array for queries with wildcards.
**Default**: false
#### sources
Add result-specific "sources" array with component source. A 0 element indicates the component is matched on the current (This) entity.
**Default**: false
#### variables
Add result-specific "variables" array with values for variables, if any.
**Default**: true
#### is_set
Add result-specific "is_set" array with boolean elements indicating whether component was matched (used for optional terms).
**Default**: false
#### values
Add result-specific "values" array with component values. A 0 element indicates a component that could not be serialized, which can be either
because no reflection data was registered, because the component has no
data, or because the query didn't request it.
**Default**: false
#### entities
Add result-specific "entities" array with matched entities.
**Default**: true
#### entity_labels
Include doc name for entities.
**Default**: false
#### entity_ids
Include numerical ids for entities.
**Default**: false
#### variable_labels
Include doc name for variables.
**Default**: false
#### variable_ids
Include numerical ids for variables.
**Default**: false
#### duration
Include measurement on how long it took to serialize result.
**Default**: false
#### type_info
Add top-level "type_info" array with reflection data on the type in
the query. If a query element has a component that has no reflection
data, a 0 element is added to the array.
**Default**: false
#### Example:
```
/query?q=Position
/query?q=Position&values=true
/query?q=Position%2CVelocity
/query?name=systems.Move
```
### stats
```
GET /stats/<category>/<period>
```
The stats endpoint returns statistics for a specified category or period. This endpoint requires the monitor module to be imported (see above). The supported categories are:
- `world`
- `pipeline`
The supported periods are:
- 1s
- 1m
- 1h
- 1d
- 1w

View File

@@ -0,0 +1,875 @@
# Systems
Systems are queries + a function that can be ran manually or get scheduled as part of a pipeline. To use systems, applications must build Flecs with the `FLECS_SYSTEM` addon (enabled by default).
An example of a simple system:
```c
// System implementation
void Move(ecs_iter_t *it) {
// Get fields from system query
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
// Iterate matched entities
for (int i = 0; i < it->count, i++) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
}
// System declaration
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity);
```
```cpp
// System declaration
flecs::system sys = world.system<Position, const Velocity>("Move")
.each([](Position& p, const Velocity &v) {
// Each is invoked for each entity
p.x += v.x;
p.y += v.y;
});
```
In C, a system can also be created with the `ecs_system_init` function / `ecs_system` shorthand which provides more flexibility. The same system can be created like this:
```c
ecs_entity_t ecs_id(Move) = ecs_system(world, {
.entity = ecs_entity(world, { /* ecs_entity_desc_t */
.name = "Move",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = { /* ecs_filter_desc_t::terms */
{ ecs_id(Position) },
{ ecs_id(Velocity), .inout = EcsIn }
}
.callback = Move
})
```
To manually run a system, do:
```c
ecs_run(world, ecs_id(Move), 0.0 /* delta_time */, NULL /* param */)
```
```cpp
sys.run();
```
By default systems are registered for a pipeline which orders systems by their "phase" (`EcsOnUpdate`). To run all systems in a pipeline, do:
```c
ecs_progress(world, 0 /* delta_time */);
```
```cpp
world.progress();
```
To run systems as part of a pipeline, applications must build Flecs with the `FLECS_PIPELINE` addon (enabled by default). To prevent a system from being registered as part of a pipeline, specify 0 as phase:
```c
ECS_SYSTEM(world, Move, 0, Position, [in] Velocity);
```
```cpp
flecs::system sys = world.system<Position, const Velocity>("Move")
.kind(0)
.each([](Position& p, const Velocity &v) { /* ... */ });
```
## System Iteration
Because systems use queries, the iterating code looks similar:
```c
// Query iteration
ecs_iter_t it = ecs_query_iter(world, q);
// Iterate tables matched by query
while (ecs_query_next(&it)) {
// Get fields from query
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
// Iterate matched entities
for (int i = 0; i < it->count, i++) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
}
// System code
void Move(ecs_iter_t *it) {
// Get fields from system query
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
// Iterate matched entities
for (int i = 0; i < it->count, i++) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
}
```
Note how query iteration has an outer and an inner loop, whereas system iteration only has the inner loop. The outer loop for systems is iterated by the `ecs_run` function, which invokes the system function. When running a pipeline, this means that a system callback can be invoked multiple times per frame, once for each matched table.
In C++ system and query iteration uses the same `each`/`iter` functions:
```cpp
// Query iteration (each)
q.each([](Position& p, const Velocity &v) { /* ... */ });
// System iteration (each)
world.system<Position, const Velocity>("Move")
.each([](Position& p, const Velocity &v) { /* ... */ });
```
```cpp
// Query iteration (iter)
q.iter([](flecs::iter& it, Position *p, const Velocity *v) {
for (auto i : it) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
});
// System iteration (iter)
world.system<Position, const Velocity>("Move")
.iter([](flecs::iter& it, Position *p, const Velocity *v) {
for (auto i : it) {
p[i].x += v[i].x;
p[i].y += v[i].y;
}
});
```
Just like in the C API, the `iter` function can be invoked multiple times per frame, once for each matched table. The `each` function is called once per matched entity.
Note that there is no significant performance difference between `iter` and `each`, which can both be vectorized by the compiler. By default `each` can actually end up being faster, as it is instanced (see [query manual](Queries.md#each-c)).
## Using delta_time
A system provides a `delta_time` which contains the time passed since the last frame:
```c
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count, i++) {
p[i].x += v[i].x * it->delta_time;
p[i].y += v[i].y * it->delta_time;
}
```
```cpp
world.system<Position, const Velocity>("Move")
.iter([](flecs::iter& it, size_t, Position& p, const Velocity &v) {
p.x += v.x * it.delta_time();
p.y += v.y * it.delta_time();
});
world.system<Position, const Velocity>("Move")
.iter([](flecs::iter& it, Position *p, const Velocity *v) {
for (auto i : it) {
p[i].x += v[i].x * it.delta_time();
p[i].y += v[i].y * it.delta_time();
}
});
```
This is the value passed into `ecs_progress`:
```c
ecs_progress(world, delta_time);
```
```cpp
world.progress(delta_time);
```
Passing a value for `delta_time` is useful if you're running `progress()` from within an existing game loop that already has time management. Providing 0 for the argument, or omitting it in the C++ API will cause `progress()` to measure the time since the last call and use that as value for `delta_time`:
```c
ecs_progress(world, 0);
```
```cpp
world.progress();
```
A system may also use `delta_system_time`, which is the time elapsed since the last time the system was invoked. This can be useful when a system is not invoked each frame, for example when using a timer.
## Tasks
A task is a system that matches no entities. Tasks are ran once per frame, and are useful for running code that is not related to entities. An example of a task system:
```c
// System function
void PrintTime(ecs_iter_t *it) {
printf("Time: %f\n", it->delta_time);
}
// System declaration using the ECS_SYSTEM macro
ECS_SYSTEM(world, PrintTime, EcsOnUpdate, 0);
// System declaration using the descriptor API
ecs_system(world, {
.entity = ecs_entity(world, {
.name = "PrintTime",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.callback = PrintTime
});
// Runs PrintTime
ecs_progress(world, 0);
```
```cpp
world.system("PrintTime")
.kind(flecs::OnUpdate)
.iter([](flecs::iter& it) {
printf("Time: %f\n", it.delta_time());
});
// Runs PrintTime
world.progress();
```
Tasks may query for components from a fixed source or singleton:
```c
// System function
void PrintTime(ecs_iter_t *it) {
// Get singleton component
Game *g = ecs_field(it, Game, 1);
printf("Time: %f\n", g->time);
}
// System declaration using the ECS_SYSTEM macro
ECS_SYSTEM(world, PrintTime, EcsOnUpdate, Game($));
// System declaration using the descriptor API
ecs_system(world, {
.entity = ecs_entity(world, {
.name = "PrintTime",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ .id = ecs_id(Game), .src.id = ecs_id(Game) } // Singleton source
},
.callback = PrintTime
});
```
```cpp
world.system<Game>("PrintTime")
.term_at(1).singleton()
.kind(flecs::OnUpdate)
.iter([](flecs::iter& it, Game *g) {
printf("Time: %f\n", g->time);
});
```
Note that `it->count` is always 0 for tasks, as they don't match any entities. In C++ this means that the function passed to `each` is never invoked for tasks. Applications will have to use `iter` instead when using tasks in C++.
## Pipelines
A pipeline is a list of systems that is executed when the `ecs_progress`/`world::progress` function is invoked. Which systems are part of the pipeline is determined by a pipeline query. A pipeline query is a regular ECS query, which matches system entities. Flecs has a builtin pipeline with a predefined query, in addition to offering the ability to specify a custom pipeline query.
A pipeline by default orders systems by their entity id, to ensure deterministic order. This generally means that systems will be ran in the order they are declared, as entity ids are monotonically increasing. Note that this is not guaranteed: when an application deletes entities before creating a system, the system can receive a recycled id, which means it could be lower than the last issued id. For this reason it is recommended to prevent entity deletion while registering systems. When this can't be avoided, an application can create a custom pipeline with a user-defined `order_by` function (see custom pipeline).
Pipelines may utilize additional query mechanisms for ordering, such as `cascade` or `group_by`.
In addition to a system query, pipelines also analyze the components that systems are reading and writing to determine where to insert sync points. During a sync point enqueued commands are ran, which ensures that systems after a sync point can see all mutations from before a sync point.
### Builtin Pipeline
The builtin pipeline matches systems that depend on a _phase_. A phase is any entity with the `EcsPhase`/`flecs::Phase` tag. To add a dependency on a phase, the `DependsOn` relationship is used. This happens automatically when using the `ECS_SYSTEM` macro/`flecs::system::kind` method:
```c
// System is created with (DependsOn, OnUpdate)
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity);
```
```cpp
// System is created with (DependsOn, OnUpdate)
world.system<Position, Velocity>("Move")
.kind(flecs::OnUpdate)
.each([](Position& p, Velocity& v) {
// ...
});
```
Systems are ordered using a topology sort on the `DependsOn` relationship. Systems higher up in the topology are ran first. In the following example the order of systems is `InputSystem, MoveSystem, CollisionSystem`:
```
PreUpdate
/ \
InputSystem OnUpdate
/ \
MoveSystem PostUpdate
/
CollisionSystem
```
Flecs has the following builtin phases, listed in topology order:
- `EcsOnStart`
- `EcsOnLoad`
- `EcsPostLoad`
- `EcsPreUpdate`
- `EcsOnUpdate`
- `EcsOnValidate`
- `EcsPostUpdate`
- `EcsPreStore`
- `EcsOnStore`
In C++ the phase identifiers are namespaced:
- `flecs::OnStart`
- `flecs::OnLoad`
- `flecs::PostLoad`
- `flecs::PreUpdate`
- `flecs::OnUpdate`
- `flecs::OnValidate`
- `flecs::PostUpdate`
- `flecs::PreStore`
- `flecs::OnStore`
The `EcsOnStart`/`flecs::OnStart` phase is a special phase that is only ran the first time `progress()` is called.
An application can create custom phases, which can be (but don't need to be) branched off of existing ones:
```c
// Phases must have the EcsPhase tag
ecs_entity_t Physics = ecs_new_w_id(ecs, EcsPhase);
ecs_entity_t Collisions = ecs_new_w_id(ecs, EcsPhase);
// Phases can (but don't have to) depend on other phases which forces ordering
ecs_add_pair(ecs, Physics, EcsDependsOn, EcsOnUpdate);
ecs_add_pair(ecs, Collisions, EcsDependsOn, Physics);
// Custom phases can be used just like regular phases
ECS_SYSTEM(world, Collide, Collisions, Position, Velocity);
```
#### Builtin Pipeline Query
The builtin pipeline query looks like this:
Query DSL:
```
flecs.system.System, Phase(cascade(DependsOn)), !Disabled(up(DependsOn)), !Disabled(up(ChildOf))
```
C descriptor API:
```c
ecs_pipeline_init(world, &(ecs_pipeline_desc_t){
.query.filter.terms = {
{ .id = EcsSystem },
{ .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn },
{ .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot },
{ .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot }
}
});
```
C++ builder API:
```c
world.pipeline()
.with(flecs::System)
.with(flecs::Phase).cascade(flecs::DependsOn)
.without(flecs::Disabled).up(flecs::DependsOn)
.without(flecs::Disabled).up(flecs::ChildOf)
.build();
```
### Custom pipeline
Applications can create their own pipelines which fully customize which systems are matched, and in which order they are executed. Custom pipelines can use phases and `DependsOn`, or they may use a completely different approach. This example shows how to create a pipeline that matches all systems with the `Foo` tag:
```c
ECS_TAG(world, Foo);
// Create custom pipeline
ecs_entity_t pipeline = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){
.query.filter.terms = {
{ .id = EcsSystem }, // mandatory
{ .id = Foo }
}
});
// Configure the world to use the custom pipeline
ecs_set_pipeline(world, pipeline);
// Create system
ECS_SYSTEM(world, Move, Foo, Position, Velocity);
// Runs the pipeline & system
ecs_progress(world, 0);
```
```cpp
// Create custom pipeline
flecs::entity pipeline = world.pipeline()
.with(flecs::System)
.with(Foo) // or .with<Foo>() if a type
.build();
// Configure the world to use the custom pipeline
world.set_pipeline(pipeline);
// Create system
auto move = world.system<Position, Velocity>("Move")
.kind(Foo) // or .kind<Foo>() if a type
.each(...);
// Runs the pipeline & system
world.progress();
```
Note that `ECS_SYSTEM` kind parameter/`flecs::system::kind` add the provided entity both by itself as well as with a `DependsOn` relationship. As a result, the above `Move` system ends up with both:
- `Foo`
- `(DependsOn, Foo)`
This allows applications to still use the macro/builder API with custom pipelines, even if the custom pipeline does not use the `DependsOn` relationship. To avoid adding the `DependsOn` relationship, `0` can be passed to `ECS_SYSTEM` or `flecs::system::kind` followed by adding the tag manually:
```c
ecs_add(world, Move, Foo);
```
```cpp
move.add(Foo);
```
#### Pipeline switching performance
When running a multithreaded application, switching pipelines can be an expensive operation. The reason for this is that it requires tearing down and recreating the worker threads with the new pipeline context. For this reason it can be more efficient to use queries that allow for enabling/disabling groups of systems vs. switching pipelines.
For example, the builtin pipeline excludes groups of systems from the schedule that:
- have the `Disabled` tag
- have a parent (module) with the `Disabled` tag
- depend on a phase with the `Disabled` tag
### Disabling systems
Because pipelines use regular ECS queries, adding the `EcsDisabled`/`flecs::Disabled` tag to a system entity will exclude the system from the pipeline. An application can use the `ecs_enable` function or `entity::enable`/`entity::disable` methods to enable/disable a system:
```c
// Disable system in C
ecs_enable(world, Move, false);
// Enable system in C
ecs_enable(world, Move, true);
```
```cpp
// Disable system in C++
s.disable();
// Enable system in C++
s.enable();
```
Additionally the `EcsDisabled`/`flecs::Disabled` tag can be added/removed directly:
```c
ecs_add_id(world, Move, EcsDisabled);
```
```c
s.add(flecs::Disabled);
```
Note that this applies both to builtin pipelines and custom pipelines, as entiites with the `Disabled` tag are ignored by default by queries.
Phases can also be disabled when using the builtin pipeline, which excludes all systems that depend on the phase. Note that is transitive, if `PhaseB` depends on `PhaseA` and `PhaseA` is disabled, systems that depend on both `PhaseA` and `PhaseB` will be excluded from the pipeline. For this reason, the builtin phases don't directly depend on each other, so that disabling `EcsOnUpdate` does not exclude systems that depend on `EcsPostUpdate`.
When the parent of a system is disabled, it will also be excluded from the builin pipeline. This makes it possible to disable all systems in a module with a single operation.
## Staging
When calling `progress()` the world enters a readonly state in which all ECS operations like `add`, `remove`, `set` etc. are enqueued as commands (called "staging"). This makes sure that it is safe for systems to iterate component arrays while enqueueing operations. Without staging, component storage arrays could be reallocated to a different memory location, which could cause system code to crash. Additionally, enqueueing operations makes it safe for multiple threads to iterate the same world without taking locks as thread gets its own command queue.
In general the framework tries its best to make sure that running code inside a system doesn't have different results than running it outside of a system, but because operations are enqueued as commands, this is not always the case. For example, the following code would return true outside of a system, but false inside of a system:
```cpp
if (!e.has<Tag>()) {
e.add<Tag>();
return e.has<Tag>();
}
```
Note that commands are only enqueued for ECS operations like `add`, `remove`, `set` etc. Reading or writing a queried for component directly does not enqueue commands. As a rule of thumb, anything that does not require calling an ECS function/method does not enqueue a command.
There are a number of things applications can do to force merging of operations, or to prevent operations from being enqueued as commands. To decide which mechanism to use, an application has to decide whether it needs:
1. Commands to be merged before another system
2. Operations not to be enqueued as commands.
The mechanisms to accomplish this are sync points for 1), and `no_readonly` systems for 2).
### Sync points
Sync points are moments during the frame where all commands are flushed to the storage. Systems that run after a sync point will be able to see all operations that happened up until the sync point. Sync points are inserted automatically by analyzing which commands could have been inserted and which components are being read by systems.
Because Flecs can't see inside the implementation of a system, pipelines can't know for which components a system could insert commands. This means that by default a pipeline assumes that systems insert no commands / that it is OK for commands to be merged at the end of the frame. To get commands to merge sooner, systems must be annotated with the components they write.
A pipeline tracks on a per-component basis whether commands could have been inserted for it, and when a component is being read. When a pipeline sees a read for a component for which commmands could have been inserted, a sync point is inserted before the system that reads. This ensures that sync points are only inserted when necessary:
- Multiple systems that enqueue commands can run before a sync point, possibly combining commands for multiple reads
- When a system is inactive (e.g. it doesn't match any entities) or is disabled, it will be ignored for sync point insertion
- Different combinations of modules have different sync requirements, automatic sync point insertion ensures that sync points are only inserted for the set of systems that are imported and are active.
To make the scheduler aware that a system can enqueue commands for a component, use the `out` modifier in combination with matching a component on an empty entity (`0`). This tells the scheduler that even though a system is not matching with the component, it is still "writing" it:
```c
// The '()' means, don't match this component on an entity, while `[out]` indicates
// that the component is being written. This is interpreted by pipelines as a
// system that can potentially enqueue commands for the Transform component.
ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [out] Transform());
```
```c
// When using the descriptor API for creating the system, set the EcsIsEntity
// flag while leaving the id field to 0. This is equivalent to doing `()` in the DSL.
ecs_system(world, {
.query.filter.terms = {
{ ecs_id(Position) },
{
.inout = EcsOut,
.id = ecs_id(Transform),
.src.flags = EcsIsEntity,
.src.id = 0 /* Default value */
}
},
/* ... */
});
```
```cpp
// In the C++ API, use the write method to indicate commands could be inserted.
world.system<Position>()
.write<Transform>()
.each( /* ... */);
```
This will cause insertion of a sync point before the next system that reads `Transform`. Similarly, a system can also be annotated with reading a component that it doesn't match with, which is useful when a system calls `get`:
```c
ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [in] Transform());
```
```c
ecs_system(world, {
.query.filter.terms = {
{ ecs_id(Position) },
{
.inout = EcsIn,
.id = ecs_id(Transform),
.src.flags = EcsIsEntity,
.src.id = 0 /* Default value */
}
},
/* ... */
});
```
```cpp
// In the C++ API, use the read method to indicate a component is read using .get
world.system<Position>()
.read<Transform>()
.each( /* ... */);
```
### No readonly systems
By default systems are ran while the world is in "readonly" mode, where all ECS operations are enqueued as commands. Note that readonly mode only applies to "structural" changes, such as changing the components of an entity or other operations that mutate ECS data structures. Systems can still write component values while in readonly mode.
In some cases however, operations need to be immediately visible to a system. A typical example is a system that assigns tasks to resources, like assigning plates to a waiter. A system should only assign plates to a waiter that hasn't been assigned any plates yet, but to know which waiters are free, the operation that assigns a plate to a waiter must be immediately visible.
To accomplish this, systems can be marked with the `no_readonly` flag, which signals that a system should be ran while the world is not in readonly mode. This causes ECS operations to not get enqueued, and allows the system to directly see the results of operations. There are a few limitations to `no_readonly` systems:
- `no_readonly` systems are always single threaded
- operations on the iterated over entity must still be deferred
The reason for the latter limitation is that allowing for operations on the iterated over entity would cause the system to modify the storage it is iterating, which could cause undefined behavior similar to what you'd see when changing a vector while iterating it.
The following example shows how to create a `no_readonly` system:
```c
ecs_system(ecs, {
.entity = ecs_entity(ecs, {
.name = "AssignPlate",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ .id = Plate },
},
.callback = AssignPlate,
.no_readonly = true // disable readonly mode for this system
});
```
```cpp
ecs.system("AssignPlate")
.with<Plate>()
.no_readonly() // disable readonly mode for this system
.iter([&](flecs::iter& it) { /* ... */ })
```
This ensures the world is not in readonly mode when the system is ran. Operations are however still enqueued as commands, which ensures that the system can enqueue commands for the entity that is being iterated over. To prevent commands from being enqueued, a system needs to suspend and resume command enqueueing. This is an extra step, but makes it possible for a system to both enqueue commands for the iterated over entity, as well as do operations that are immediately visible. An example:
```c
void AssignPlate(ecs_iter_t *it) {
for (int i = 0; i < it->count; i ++) {
// ECS operations ran here are visible after running the system
ecs_defer_suspend(it->world);
// ECS operations ran here are immediately visible
ecs_defer_resume(it->world);
// ECS operations ran here are visible after running the system
}
}
```
```cpp
.iter([](flecs::iter& it) {
for (auto i : it) {
// ECS operations ran here are visible after running the system
it.world().defer_suspend();
// ECS operations ran here are immediately visible
it.world().defer_resume();
// ECS operations ran here are visible after running the system
}
});
```
Note that `defer_suspend` and `defer_resume` may only be called from within a `no_readonly` system.
### Threading
Systems in Flecs can be multithreaded. This requires both the system to be created as a multithreaded system, as well as configuring the world to have a number of worker threads. To create worker threads, use the `set_threads` function:
```c
ecs_set_threads(world, 4); // Create 4 worker threads
```
```cpp
world.set_threads(4);
```
To create a multithreaded system, use the `multi_threaded` flag:
```c
ecs_system(ecs, {
.entity = ecs_entity(ecs, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ .id = ecs_id(Position) }
},
.callback = Dummy,
.multi_threaded = true // run system on multiple threads
});
```
```cpp
world.system<Position>()
.multi_threaded()
.each( /* ... */ );
```
By default systems are created as single threaded. Single threaded systems are always ran on the main thread. Multithreaded systems are ran on all worker threads. The scheduler runs each multithreaded system on all threads, and divides the number of matched entities across the threads. The way entities are allocated to threads ensures that the same entity is always processed by the same thread, until the next sync point. This means that in an ideal case, all systems in a frame can run until completion without having to synchronize.
The way the scheduler ensures that the same entities are processed by the same threads is by slicing up the entities in a table into N slices, where N is the number of threads. For a table that has 1000 entities, the first thread will process entities 0..249, thread 2 250..499, thread 3 500..749 and thread 4 entities 750..999. For more details on this behavior, see `ecs_worker_iter`/`flecs::iterable::worker_iter`.
### Threading with Async Tasks
Systems in Flecs can also be multithreaded using an external asynchronous task system. Instead of creating regular worker threads using `set_threads`, use the `set_task_threads` function and provide the OS API callbacks to create and wait for task completion using your job system.
This can be helpful when using Flecs within an application which already has a job queue system to handle multithreaded tasks.
```c
ecs_set_tasks_threads(world, 4); // Create 4 worker task threads for the duration of each ecs_progress update
```
```cpp
world.set_task_threads(4);
```
For simplicity, these task callbacks use the same format as Flecs `ecs_os_api_t` thread APIs. In fact, you could provide your regular os thread api functions to create short-duration threads for multithreaded system processing.
Create multithreaded systems using the `multi_threaded` flag as with `ecs_set_threads` above.
When `ecs_progress` is called, your `ecs_os_api.task_new_` callback will be called once for every task thread needed to create task threads on demand. When `ecs_progress` is complete, your `ecs_os_api.task_join_` function will be called to clean up each task thread.
By providing callback functions which create and remove tasks for your specific asynchronous task system, you can use Flecs with any kind of async task management scheme.
The only limitation is that your async task manager must be able to create and execute the number of simultaneous tasks specified in `ecs_set_task_threads` and must exist for the duration of `ecs_progress`.
## Timers
When running a pipeline, systems are ran each time `progress()` is called. The `FLECS_TIMER` addon makes it possible to run systems at a specific time interval or rate.
### Interval
The following example shows how to run systems at a time interval:
```c
// Using the ECS_SYSTEM macro
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity);
ecs_set_interval(world, ecs_id(Move), 1.0); // Run at 1Hz
```
```c
// Using the ecs_system_init function/ecs_system macro:
ecs_system(world, {
.entity = ecs_entity(world, {
.name = "Move",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ ecs_id(Position) },
{ ecs_id(Velocity), .inout = EcsIn }
},
.callback = Move,
.interval = 1.0 // Run at 1Hz
});
```
```cpp
world.system<Position, const Velocity>()
.interval(1.0) // Run at 1Hz
.each(...);
```
### Rate
The following example shows how to run systems every Nth tick with a rate filter:
```c
// Using the ECS_SYSTEM macro
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity);
ecs_set_rate(world, ecs_id(Move), 2); // Run every other frame
```
```c
// Using the ecs_system_init function/ecs_system macro:
ecs_system(world, {
.entity = ecs_entity(world, {
.name = "Move",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ ecs_id(Position) },
{ ecs_id(Velocity), .inout = EcsIn }
},
.callback = Move,
.rate = 2.0 // Run every other frame
});
```
```cpp
world.system<Position, const Velocity>()
.rate(2) // Run every other frame
.each(...);
```
### Tick source
Instead of setting the interval or rate directly on the system, an application may also create a separate entity that holds the time or rate filter, and use that as a "tick source" for a system. This makes it easier to use the same settings across groups of systems:
```c
// Using the ECS_SYSTEM macro
ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity);
// Passing 0 to entity argument will create a new entity. Similarly a rate
// filter entity could be created with ecs_set_rate(world, 0, 2)
ecs_entity_t tick_source = ecs_set_interval(world, 0, 1.0);
// Set tick source for system
ecs_set_tick_source(world, ecs_id(Move), tick_source);
```
```c
// Passing 0 to entity argument will create a new entity. Similarly a rate
// filter entity could be created with ecs_set_rate(world, 0, 2)
ecs_entity_t tick_source = ecs_set_interval(world, 0, 1.0);
// Using the ecs_system_init function/ecs_system macro:
ecs_system(world, {
.entity = ecs_entity(world, {
.name = "Move",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ ecs_id(Position) },
{ ecs_id(Velocity), .inout = EcsIn }
},
.callback = Move,
.tick_source = tick_source // Set tick source for system
});
```
```cpp
// A rate filter can be created with .rate(2)
flecs::entity tick_source = world.timer()
.interval(1.0);
world.system<Position, const Velocity>()
.tick_source(tick_source) // Set tick source for system
.each(...);
```
Interval filters can be paused and resumed:
```c
// Pause timer
ecs_stop_timer(world, tick_source);
// Resume timer
ecs_start_timer(world, tick_source);
```
```cpp
// Pause timer
tick_source.stop();
// Resume timer
tick_source.start();
```
An additional advantage of using shared interval/rate filter between systems is that it guarantees that systems are ran at the same tick. When a system is disabled, its interval/rate filters aren't updated, which means that when the system is reenabled again it would be out of sync with other systems that had the same interval/rate. specified. When using a shared tick source however the system is guaranteed to run at the same tick as other systems with the same tick source, even after the system is reenabled.
### Nested tick sources
One tick source can be used as the input of another (rate) tick source. The rate tick source will run at each Nth tick of the input tick source. This can be used to create nested tick sources, like in the following example:
```c
// Tick at 1Hz
ecs_entity_t each_second = ecs_set_interval(world, 0, 1.0);
// Tick each minute
ecs_entity_t each_minute = ecs_set_rate(world, 0, 60);
ecs_set_tick_source(world, each_minute, each_second);
// Tick each hour
ecs_entity_t each_hour = ecs_set_rate(world, 0, 60);
ecs_set_tick_source(world, each_hour, each_minute);
```
```cpp
// Tick at 1Hz
flecs::entity each_second = world.timer()
.interval(1.0);
// Tick each minute
flecs::entity each_minute = world.timer()
.rate(60, each_second);
// Tick each hour
flecs::entity each_hour = world.timer()
.rate(60, each_minute);
```
Systems can also act as each others tick source:
```c
// Tick at 1Hz
ecs_entity_t each_second = ecs_system(world, {
.entity = ecs_entity(world, {
.name = "EachSecond",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.callback = Dummy,
.interval = 1.0
});
// Tick each minute
ecs_entity_t each_minute = ecs_system(world, {
.entity = ecs_entity(world, {
.name = "EachMinute",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.callback = Dummy,
.tick_source = each_second,
.rate = 60
});
// Tick each hour
ecs_entity_t each_hour = ecs_system(world, {
.entity = ecs_entity(world, {
.name = "EachHour",
.add = { ecs_dependson(EcsOnUpdate) }
}),
.callback = Dummy,
.tick_source = each_minute,
.rate = 60
});
```
```cpp
// Tick at 1Hz
flecs::entity each_second = world.system("EachSecond")
.interval(1.0)
.iter([](flecs::iter& it) { /* ... */ });
// Tick each minute
flecs::entity each_minute = world.system("EachMinute")
.tick_source(each_second)
.rate(60)
.iter([](flecs::iter& it) { /* ... */ });
// Tick each hour
flecs::entity each_hour = world.system("EachHour")
.tick_source(each_minute)
.rate(60)
.iter([](flecs::iter& it) { /* ... */ });
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");
.github-corner svg {
fill: var(--primary-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.next_section_button {
display: block;
padding: var(--spacing-large) 0 var(--spacing-small) 0;
color: var(--page-background-color);
user-select: none;
}
.alter-theme-button:hover {
background: var(--primary-dark-color);
}
html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */
html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
filter: brightness(87%) hue-rotate(180deg) invert();
}
.bordered_image {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
display: inline-block;
overflow: hidden;
}
html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */
html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
border-radius: var(--border-radius-small);
}
html {
--primary-color: #42b983;
--fragment-link: #42b983;
--fragment-keywordflow: #ff7b72;
--spacing-small: 8px;
--content-maxwidth: 800px;
--font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
--font-family-monospace: 'Roboto Mono', Monaco, courier, monospace;
--fragment-lineheight: 24px;
}
html.dark-mode {
--primary-color: #42b983;
--page-background-color: #0f1116;
--side-nav-background: #1a1d23;
--separator-color: rgba(255,255,255,0.07);
--tablehead-background: #1a1d23;
--code-background: #1a1d23;
--fragment-background: #1a1d23;
--fragment-link: #42b983;
--fragment-keywordflow: #ff7b72;
}
div.fragment {
padding-top: 25px !important;
padding-bottom: 25px !important;
padding-left: 20px !important;
border-radius: 0px;
border: none;
}
code {
border-radius: 0px;
border: none;
}
#projectlogo img {
max-height: calc(var(--title-font-size) * 1.5) !important;
}
html.light-mode #projectlogo img {
content: url(logo_small_dark.png);
}

View File

@@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);

View File

@@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@@ -0,0 +1,81 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeInteractiveToc {
static topOffset = 38
static hideMobileMenu = true
static headers = []
static init() {
window.addEventListener("load", () => {
let toc = document.querySelector(".contents > .toc")
if(toc) {
toc.classList.add("interactive")
if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {
toc.classList.add("open")
}
document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => {
if(toc.classList.contains("open")) {
toc.classList.remove("open")
} else {
toc.classList.add("open")
}
})
document.querySelectorAll(".contents > .toc > ul a").forEach((node) => {
let id = node.getAttribute("href").substring(1)
DoxygenAwesomeInteractiveToc.headers.push({
node: node,
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll", () => {
DoxygenAwesomeInteractiveToc.update()
})
})
DoxygenAwesomeInteractiveToc.update()
}
})
}
static update() {
let active = DoxygenAwesomeInteractiveToc.headers[0]?.node
DoxygenAwesomeInteractiveToc.headers.forEach((header) => {
let position = header.headerNode.getBoundingClientRect().top
header.node.classList.remove("active")
header.node.classList.remove("aboveActive")
if(position < DoxygenAwesomeInteractiveToc.topOffset) {
active = header.node
active?.classList.add("aboveActive")
}
})
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
}

View File

@@ -0,0 +1,51 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeParagraphLink {
// Icon from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
static title = "Permanent Link"
static init() {
$(function() {
$(document).ready(function() {
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
let anchorlink = document.createElement("a")
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
anchorlink.classList.add("anchorlink")
node.classList.add("anchor")
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
node.parentElement.appendChild(anchorlink)
})
})
})
}
}

View File

@@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}

View File

@@ -0,0 +1,115 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
--toc-sticky-top: -25px;
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
<!-- HTML footer for doxygen 1.9.5-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!-- HTML header for doxygen 1.9.5-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<script type="text/javascript">var page_layout=1;</script>
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
$darkmode
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link rel="icon" href="logo_small.png" />
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript">
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeParagraphLink.init()
DoxygenAwesomeInteractiveToc.init()
</script>
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/SanderMertens/flecs" class="github-corner" title="View source on GitHub" target="_blank">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<style>
.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
</style>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign">
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

View File

@@ -0,0 +1,9 @@
cc_library(
name = "os-api",
visibility = ["//:__subpackages__"],
deps = ["//:flecs", "@bake//:util"],
srcs = glob(["os_api/flecs-os_api-bake/src/**/*.c", "os_api/flecs-os_api-bake/src/**/*.h"]),
hdrs = glob(["os_api/flecs-os_api-bake/include/**/*.h"]),
includes = ["os_api/flecs-os_api-bake/include"],
)

View File

@@ -0,0 +1,58 @@
# Examples
This folder contains code examples for the C and C++ APIs.
## Build with cmake
To build the examples with cmake, run the following commands from the root of the repository:
```
cd examples/c
mkdir cmake_build
cd cmake_build
cmake ..
cmake --build .
```
To build the C++ examples, run the same commands from examples/cpp:
```
cd examples/cpp
mkdir cmake_build
cd cmake_build
cmake ..
cmake --build .
```
This will produce two binaries for each example: one that links with a shared Flecs library, and one that is linked with static Flecs library. The binaries linked with the static library have the postfix _static.
To run a single example, execute it from the `cmake_build` directory:
```
./entities_basics_static
```
## Build with bake
To build the examples with bake, run bake once in the root of the repository, so it knows where to find Flecs:
```
bake
```
To run a single example, use the following command:
```
bake run examples/c/entities/basics
```
To run a single example with optimizations enabled, use the following command:
```
bake run examples/c/entities/basics --cfg release
```
To build all examples, run the following command:
```
bake examples
```
See the [bake repository](https://github.com/SanderMertens/bake) for instructions on how to install bake.

View File

@@ -0,0 +1,7 @@
package(default_visibility = ["//visibility:public"])
cc_binary(
name = "example",
srcs = ["main.cpp"],
deps = ["@flecs//:flecs"]
)

View File

@@ -0,0 +1,22 @@
You can include the flecs repository in your `WORKSPACE` with:
```bazel
git_repository(
name = "flecs",
remote = "https://github.com/SanderMertens/flecs",
commit = "f150d96ba9ea8be2b24dbf2217368c231cb17720", # v2.3.2+merge329
shallow_since = "1615075784 -0800",
)
```
And then add it to your `BUILD` with:
```bazel
deps = ["@flecs//:flecs"]
```
This directory contains a complete example of this usage. To try it you can run the following from your terminal:
```
bazel run example
```

View File

@@ -0,0 +1,8 @@
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "flecs",
remote = "https://github.com/SanderMertens/flecs",
commit = "f150d96ba9ea8be2b24dbf2217368c231cb17720", # v2.3.2+merge329
shallow_since = "1615075784 -0800",
)

Some files were not shown because too many files have changed in this diff Show More