Update Introduction
parent
60b6dad87b
commit
0b7ee494eb
1 changed files with 8 additions and 8 deletions
|
|
@ -4,15 +4,15 @@ This page will introduce to you main idea behind **Bubel ECS** (**BECS**).
|
|||
|
||||
**Bubel ECS** (as name stands) is a library which implements Entity-component-system architectural pattern. This page will be about library itself, so if you don't know what is ECS pattern you can find information here [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system).
|
||||
|
||||
The core idea behind **BECS** technology is way how it's managing memory and systems. **Entities** are grouped by **types** (**entity Type**). Each **entity type** consists of components. Component duplicates are not allowed which means that e.g. you can't have two CVelocity components (assuming that "CVelocity" is component name). Entities are allocated in memory chunks, each **entity type** has list of chunks with his entities. Each block has following memory layout:
|
||||
The core idea behind **BECS** technology is the way how it is managing memory and systems. **Entities** are grouped by **types** (**entity Type**). Each **entity type** consists of components. Component duplicates are not allowed which means that e.g. you can't have two CVelocity components (assuming that "CVelocity" is component name). Entities are allocated in memory chunks, each **entity type** has list of chunks with his entities. Each block has following memory layout:
|
||||
| Meta data | EntityIDs | Components 1| Components 2 | ... |
|
||||
| ------- | ------ | ------ | ------ | ------ |
|
||||
|
||||
This layout provides fast memory access thanks to good cache locality.
|
||||
|
||||
Library provides you possibility to change entity components. That operation means that entity changing it's **entity type** and memory needs to be copied to different location in memory (into chunk of new **entity type**). Even then this operation is fairly fast and can be used for infrequently logic changes.
|
||||
Library provides a possibility to add/remove entity components. That operation means that entity changes it's **entity type** and memory needs to be copied to different location (into chunk of new **entity type**). This operation is quick enough to be used for infrequent logic changes.
|
||||
|
||||
Systems execution is managed by library itself. You as a developer gives the **BECS** information about system priority, components and dependencies. Systems are grouped into **passes**. The purpose for **passes** is to give possibility to run different systems with different frequency (e.g. default pass, physics pass called multiple times per frame (fixed time step) and then rendering pass). Passes calls all systems assigned to them in order designated by priorities. This type of systems execution model gives library a lot information which helps with generation optimal performance.
|
||||
Systems execution is managed by library itself. Developer gives the **BECS** information about system priority, components and dependencies. Systems are grouped into **passes**. The purpose for **passes** is to give possibility to run different systems with different frequency (e.g. default pass run once per frame, physics pass called multiple times per frame (fixed time step) and then rendering pass). Passes call all systems assigned to them in order designated by priorities. This type of systems execution model gives library a lot information which helps to improve performance.
|
||||
|
||||
Systems works on components, not entities. They iterate over each filtered entity processing selected components. Entities are filtered by components they have. There are several properties for components in system:
|
||||
- Required - it's default attribute for component. System don't iterate over entities which doesn't have these components.
|
||||
|
|
@ -20,21 +20,21 @@ Systems works on components, not entities. They iterate over each filtered entit
|
|||
- Excluded - system will not process entities which have these components
|
||||
Besides that attributes you can use **filterEntity** callback to filter entities as you like (filtering is done ahead of time in system registration process).
|
||||
|
||||
Next important thing is how entity are referenced. System working on raw components data, but sometimes you need to get entity data for outside of system or from different entity that is executed right now. In this kind of situation you can use **EntityID**. This IDs consists of two values: index and counter. First value is and index into linear array of pointer to entities. Counter is incrementing every time when entity is destroyed, it's used to determine if index is still valid. This gives you possibility to safely check if entity behind **EntityID** still exists. Using different methods like simple pointer to memory is disallowed as position in memory is valid only during single pass. Entity position in memory is position of **Entity** which contain only **EntityID**. Because memory chunks are aligned to some value, library is able to calculate meta data of block from pointer to entity and then pointer to each component. Using direct access by **EntityID** is considered to be fast, but a lot slower than access from system.
|
||||
Next important thing is how entity is referenced. System work on raw components data, but sometimes you need to get entity data outside of system or from different entity that is executed right now. In this kind of situation you can use **EntityID**. This ID consists of two values: index and counter. First value is and index into linear array of pointer to entities. Counter is incrementing every time when entity is destroyed, it's used to determine if index is still valid. This gives you possibility to safely check if entity behind **EntityID** still exists. Using different methods like simple pointer to memory is disallowed as position in memory is valid only during single pass. Entity position in memory is position of **Entity** which contain only **EntityID**. Because memory chunks are aligned to some value, library is able to calculate meta data of block from pointer to entity and then pointer to each component. Using direct access by **EntityID** is considered to be fast, but a lot slower than access from system.
|
||||
|
||||
Adding new entities requires using **templates**. **Template** is assigned to **entity type**. You can use single template for adding entities on multiple threads.
|
||||
|
||||
# Multithreading
|
||||
|
||||
**BECS** has system to automatically generating jobs for multithreaded execution. Task of developer is to tag components inside systems as **readonly** or **writable** and setting proper priorities for systems. Library will automatically generate jobs and dependencies in lockless fashion. There is also possibility to specify external dependencies (readonly and writable) which can be used to synchronize non-ECS memory access (e.g. octree, renderer).
|
||||
**BECS** has system to automatically generate jobs for multithreaded execution. Task of developer is to tag components inside systems as **readonly** or **writable** and setting proper priorities for systems. Library will automatically generate jobs and dependencies in lockless fashion. There is also possibility to specify external dependencies (readonly and writable) which can be used to synchronize non-ECS memory access (e.g. octree, renderer).
|
||||
|
||||
Adding new entities is thread-safe which means you can add entities from multiple threads same time. Adding entities and assigning their IDs is managed using atomic operations so it's fast (but don't over use that, if you want only to spawn hundreds of entities in one system without any logic behind that, it can be slower that singlethreaded code). Entities can be accessed immediately after beding added.
|
||||
Adding new entities is thread-safe which means you can add entities from multiple threads at the same time. Adding entities and assigning their IDs is managed using atomic operations so it is fast (but don't over use that, if you want only to spawn hundreds of entities in one system without any logic behind that, it can be slower that singlethreaded code). Entities can be accessed immediately after being added.
|
||||
|
||||
Removing entities and adding/removing components from entities operations are deferred. If you remove entity component it would be removed after pass being done.
|
||||
|
||||
# Event handling
|
||||
|
||||
Entity-component-system pattern is very good when entities are processed without accessing memory of different entities, but in real case you want entities to interact together. In single-threaded mode you can safely use direct access through **EntityID** but in multithreading it's not the case if you need data coherency in access.
|
||||
Event system handling fixing not only problem of multithreading interaction but also gives more universal way of modeling game logic. In **Bubel ECS** systems don't have to know anything more than components they processing. That means that you can eaisily separate systems from each others. It makes code cleaner and building plugins simpler.
|
||||
Event system fixes not only problem of multithreading interaction but also gives more universal way of modeling game logic. In **Bubel ECS** systems don't have to know anything more than components they are processing. That means that you can easily separate systems from each others. It makes code cleaner and building plugins simpler.
|
||||
|
||||
Events are types and can be handled by systems. Event consists of EntityID and custom data. System doesn't have to know who spawn event, it only knows about components and events which it's processing.
|
||||
Events are types and can be handled by systems. Event consists of EntityID and custom data. System doesn't have to know who spawned event, it only knows about components and events which it's processing.
|
||||
Loading…
Add table
Add a link
Reference in a new issue