Properly link flecs library
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
1
engine/libs/flecs/.bazelignore
Normal file
@@ -0,0 +1 @@
|
||||
examples/build/bazel
|
||||
9
engine/libs/flecs/.editorconfig
Normal 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
@@ -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']
|
||||
24
engine/libs/flecs/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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, ...).
|
||||
14
engine/libs/flecs/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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.
|
||||
833
engine/libs/flecs/.github/workflows/ci.yml
vendored
Normal 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
|
||||
50
engine/libs/flecs/.github/workflows/pages.yml
vendored
Normal 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
@@ -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
@@ -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"],
|
||||
)
|
||||
98
engine/libs/flecs/CMakeLists.txt
Normal 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
@@ -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
@@ -0,0 +1,211 @@
|
||||

|
||||
|
||||
[](https://github.com/SanderMertens/flecs/releases)
|
||||
[](https://github.com/SanderMertens/flecs/blob/master/LICENSE)
|
||||
[](https://www.flecs.dev/flecs/md_docs_Docs.html)
|
||||
[](https://github.com/SanderMertens/flecs/actions?query=workflow%3ACI)
|
||||
[](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)):
|
||||
|
||||
[](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!
|
||||
|
||||
[](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/
|
||||
[](https://store.steampowered.com/app/1486920/Tempest_Rising/)
|
||||
|
||||
### Territory Control
|
||||
https://store.steampowered.com/app/690290/Territory_Control_2/
|
||||
[](https://store.steampowered.com/app/690290/Territory_Control_2/)
|
||||
|
||||
### The Forge
|
||||
https://github.com/ConfettiFX/The-Forge
|
||||
[](https://github.com/ConfettiFX/The-Forge)
|
||||
|
||||
### Sol Survivor
|
||||
https://nicok.itch.io/sol-survivor-demo
|
||||
[](https://nicok.itch.io/sol-survivor-demo)
|
||||
|
||||
### Equilibrium Engine
|
||||
https://github.com/clibequilibrium/EquilibriumEngine
|
||||
[](https://github.com/clibequilibrium/EquilibriumEngine)
|
||||
|
||||
### Gravitas
|
||||
https://thepunkcollective.itch.io/gravitas
|
||||
[](https://thepunkcollective.itch.io/gravitas)
|
||||
|
||||
### After Sun
|
||||
https://github.com/foxnne/aftersun
|
||||
[](https://github.com/foxnne/aftersun)
|
||||
|
||||
### Tower defense (open source demo)
|
||||
https://www.flecs.dev/tower_defense/etc ([repository](https://github.com/SanderMertens/tower_defense))
|
||||

|
||||
|
||||
### Procedural City (open source demo)
|
||||
https://www.flecs.dev/city ([repository](https://github.com/flecs-hub/city))
|
||||

|
||||
|
||||
## 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)
|
||||
52
engine/libs/flecs/WORKSPACE
Normal 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"],
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
26
engine/libs/flecs/cmake/target_default_compile_options.cmake
Normal 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()
|
||||
113
engine/libs/flecs/cmake/target_default_compile_warnings.cmake
Normal 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()
|
||||
21
engine/libs/flecs/codecov.yaml
Normal 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
|
||||
175
engine/libs/flecs/docs/DesignWithFlecs.md
Normal 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.
|
||||
37
engine/libs/flecs/docs/Docs.md
Normal 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)
|
||||
133
engine/libs/flecs/docs/FAQ.md
Normal 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!
|
||||
769
engine/libs/flecs/docs/FlecsScriptTutorial.md
Normal 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:
|
||||
|
||||
[](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:
|
||||
[](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:
|
||||

|
||||
|
||||
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:
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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`:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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!
|
||||
|
||||
[](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
|
||||
}
|
||||
```
|
||||
|
||||
[](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
|
||||
}
|
||||
```
|
||||
|
||||
[](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
|
||||
|
||||
[](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
|
||||
```
|
||||
|
||||
[](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}
|
||||
}
|
||||
```
|
||||
|
||||
[](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
|
||||
```
|
||||
|
||||
[](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}
|
||||
}
|
||||
```
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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:
|
||||
|
||||
[](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.
|
||||
|
||||
[](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}
|
||||
```
|
||||
|
||||
[](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{}
|
||||
```
|
||||
|
||||
[](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)
|
||||
638
engine/libs/flecs/docs/JsonFormat.md
Normal 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": [{
|
||||
}]
|
||||
}
|
||||
```
|
||||
1227
engine/libs/flecs/docs/Manual.md
Normal file
3120
engine/libs/flecs/docs/Queries.md
Normal file
918
engine/libs/flecs/docs/Quickstart.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
### 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>();
|
||||
```
|
||||
1414
engine/libs/flecs/docs/Relationships.md
Normal file
355
engine/libs/flecs/docs/RestApi.md
Normal 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:
|
||||
|
||||

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

|
||||
|
||||
The default query engine used by the search bar is the rules engine, which means it is possible to use features like query variables:
|
||||
|
||||

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

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

|
||||
|
||||
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
|
||||
875
engine/libs/flecs/docs/Systems.md
Normal 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) { /* ... */ });
|
||||
```
|
||||
2700
engine/libs/flecs/docs/cfg/Doxyfile
Normal file
90
engine/libs/flecs/docs/cfg/custom.css
Normal 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);
|
||||
}
|
||||
157
engine/libs/flecs/docs/cfg/doxygen-awesome-darkmode-toggle.js
Normal 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);
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
51
engine/libs/flecs/docs/cfg/doxygen-awesome-paragraph-link.js
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
115
engine/libs/flecs/docs/cfg/doxygen-awesome-sidebar-only.css
Normal 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;
|
||||
}
|
||||
}
|
||||
2405
engine/libs/flecs/docs/cfg/doxygen-awesome.css
Normal file
9
engine/libs/flecs/docs/cfg/footer.html
Normal 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>
|
||||
94
engine/libs/flecs/docs/cfg/header.html
Normal 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"> $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 -->
|
||||
BIN
engine/libs/flecs/docs/img/explorer-arguments.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
engine/libs/flecs/docs/img/explorer-query.png
Normal file
|
After Width: | Height: | Size: 1024 KiB |
BIN
engine/libs/flecs/docs/img/explorer-remote.png
Normal file
|
After Width: | Height: | Size: 969 KiB |
BIN
engine/libs/flecs/docs/img/explorer-rules.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
engine/libs/flecs/docs/img/explorer-system.png
Normal file
|
After Width: | Height: | Size: 397 KiB |
BIN
engine/libs/flecs/docs/img/explorer.png
Normal file
|
After Width: | Height: | Size: 856 KiB |
BIN
engine/libs/flecs/docs/img/filter_diagram.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
engine/libs/flecs/docs/img/flecs-add-component-flow.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
engine/libs/flecs/docs/img/flecs-architecture-overview.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
engine/libs/flecs/docs/img/flecs-quickstart-overview.png
Normal file
|
After Width: | Height: | Size: 299 KiB |
BIN
engine/libs/flecs/docs/img/flecs-remove-component-flow.png
Normal file
|
After Width: | Height: | Size: 464 KiB |
BIN
engine/libs/flecs/docs/img/flecs-staging-flow.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
engine/libs/flecs/docs/img/logo.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
engine/libs/flecs/docs/img/logo_small.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
engine/libs/flecs/docs/img/logo_small_dark.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
engine/libs/flecs/docs/img/playground.png
Normal file
|
After Width: | Height: | Size: 515 KiB |
BIN
engine/libs/flecs/docs/img/projects/after_sun.png
Normal file
|
After Width: | Height: | Size: 787 KiB |
BIN
engine/libs/flecs/docs/img/projects/city.png
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
engine/libs/flecs/docs/img/projects/equilibrium_engine.png
Normal file
|
After Width: | Height: | Size: 3.4 MiB |
BIN
engine/libs/flecs/docs/img/projects/gravitas.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
engine/libs/flecs/docs/img/projects/sol_survivor.png
Normal file
|
After Width: | Height: | Size: 668 KiB |
BIN
engine/libs/flecs/docs/img/projects/tempest_rising.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
engine/libs/flecs/docs/img/projects/territory_control.jpeg
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
engine/libs/flecs/docs/img/projects/the_forge.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
engine/libs/flecs/docs/img/projects/tower_defense.png
Normal file
|
After Width: | Height: | Size: 855 KiB |
BIN
engine/libs/flecs/docs/img/query_instancing.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
engine/libs/flecs/docs/img/relationship_traversal.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
engine/libs/flecs/docs/img/script_tutorial/tut_playground.png
Normal file
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 662 KiB |
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 437 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 419 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
After Width: | Height: | Size: 413 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 524 KiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 276 KiB |
9
engine/libs/flecs/examples/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
58
engine/libs/flecs/examples/README.md
Normal 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.
|
||||
7
engine/libs/flecs/examples/build/bazel/BUILD
Normal file
@@ -0,0 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_binary(
|
||||
name = "example",
|
||||
srcs = ["main.cpp"],
|
||||
deps = ["@flecs//:flecs"]
|
||||
)
|
||||
22
engine/libs/flecs/examples/build/bazel/README.md
Normal 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
|
||||
```
|
||||
8
engine/libs/flecs/examples/build/bazel/WORKSPACE
Normal 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",
|
||||
)
|
||||