-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add custom components to .NET scripts #352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
20f96a8
feat(scripting-component): add TypeErasedComponentArray
Thyodas 51f6062
feat(scripting-component): component registration to scripting
Thyodas c67dafd
feat(scripting-component): add TypeErased addComponent
Thyodas 5f3b13f
feat(scripting-component): update API with new addComponent
Thyodas ac87100
feat(scripting-component): make use of Components in CubeSystem example
Thyodas b22214b
feat(scripting-component): invert Entity and TypeID for API for cohesion
Thyodas fbbb223
feat(scripting-component): add missing cstring header for std::memcpy
Thyodas 7f82eca
feat(scripting-component): remove useless log for cubes
Thyodas cdd7fef
chore(scripting-component): cleanup useless code in scripts
Thyodas 665cf2e
feat(scripting-component): move function definitions to cpp Component…
Thyodas ee06eee
chore(scripting-component): rename API to respect Nx convention
Thyodas 3a147a5
feat(scripting-component): remove useless addcomponent in Demonstrate
Thyodas 74dd7ca
feat(scripting-component): remove useless std::move
Thyodas 9782bac
feat(scripting-component): add null check for NxAddComponent interop
Thyodas 4015971
fix(scripting-component): critical bug in swap
Thyodas 442a3e9
refactor(scripting-component): remove code duplication for insertRaw
Thyodas 7c761e5
fix(scripting-component): invalid casting size
Thyodas e8476e1
refactor(scripting-component): use nexo logging system in RegisterCom…
Thyodas 090ec92
refactor(scripting-component): remove useless list in ComponentBase
Thyodas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| //// ComponentArray.cpp /////////////////////////////////////////////////////// | ||
| // | ||
| // zzzzz zzz zzzzzzzzzzzzz zzzz zzzz zzzzzz zzzzz | ||
| // zzzzzzz zzz zzzz zzzz zzzz zzzz | ||
| // zzz zzz zzz zzzzzzzzzzzzz zzzz zzzz zzz | ||
| // zzz zzz zzz z zzzz zzzz zzzz zzzz | ||
| // zzz zzz zzzzzzzzzzzzz zzzz zzz zzzzzzz zzzzz | ||
| // | ||
| // Author: Guillaume HEIN | ||
| // Date: 24/06/2025 | ||
| // Description: Source file for the component array class | ||
| // | ||
| /////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| #include "ComponentArray.hpp" | ||
|
|
||
| namespace nexo::ecs { | ||
|
|
||
| TypeErasedComponentArray::TypeErasedComponentArray(const size_t componentSize, const size_t initialCapacity): m_componentSize(componentSize), m_capacity(initialCapacity) | ||
| { | ||
| if (componentSize == 0) { | ||
| throw std::invalid_argument("Component size cannot be zero"); | ||
| } | ||
|
|
||
| m_sparse.resize(m_capacity, INVALID_ENTITY); | ||
| m_dense.reserve(m_capacity); | ||
| m_componentData.reserve(m_capacity * m_componentSize); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::insert(Entity entity, const void* componentData) | ||
| { | ||
| insertRaw(entity, componentData); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::insertRaw(Entity entity, const void* componentData) | ||
| { | ||
| if (entity >= MAX_ENTITIES) | ||
| THROW_EXCEPTION(OutOfRange, entity); | ||
|
|
||
| ensureSparseCapacity(entity); | ||
|
|
||
| if (hasComponent(entity)) { | ||
| LOG(NEXO_WARN, "Entity {} already has component", entity); | ||
| return; | ||
| } | ||
|
|
||
| const size_t newIndex = m_size; | ||
| m_sparse[entity] = newIndex; | ||
| m_dense.push_back(entity); | ||
|
|
||
| // Resize component data vector if needed | ||
| size_t requiredSize = (m_size + 1) * m_componentSize; | ||
| if (m_componentData.size() < requiredSize) { | ||
| m_componentData.resize(requiredSize); | ||
| } | ||
|
|
||
| // Copy component data | ||
| std::memcpy(m_componentData.data() + newIndex * m_componentSize, | ||
| componentData, m_componentSize); | ||
|
|
||
| ++m_size; | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::remove(Entity entity) | ||
| { | ||
| if (!hasComponent(entity)) | ||
| THROW_EXCEPTION(ComponentNotFound, entity); | ||
|
|
||
| size_t indexToRemove = m_sparse[entity]; | ||
|
|
||
| // Handle grouped components | ||
| if (indexToRemove < m_groupSize) { | ||
| size_t groupLastIndex = m_groupSize - 1; | ||
| if (indexToRemove != groupLastIndex) { | ||
| swapComponents(indexToRemove, groupLastIndex); | ||
| std::swap(m_dense[indexToRemove], m_dense[groupLastIndex]); | ||
| m_sparse[m_dense[indexToRemove]] = indexToRemove; | ||
| m_sparse[m_dense[groupLastIndex]] = groupLastIndex; | ||
| } | ||
| --m_groupSize; | ||
| indexToRemove = groupLastIndex; | ||
| } | ||
|
|
||
| // Standard removal | ||
| const size_t lastIndex = m_size - 1; | ||
| if (indexToRemove != lastIndex) { | ||
| swapComponents(indexToRemove, lastIndex); | ||
| std::swap(m_dense[indexToRemove], m_dense[lastIndex]); | ||
| m_sparse[m_dense[indexToRemove]] = indexToRemove; | ||
| } | ||
|
|
||
| m_sparse[entity] = INVALID_ENTITY; | ||
| m_dense.pop_back(); | ||
| --m_size; | ||
|
|
||
| shrinkIfNeeded(); | ||
| } | ||
|
|
||
| bool TypeErasedComponentArray::hasComponent(Entity entity) const | ||
| { | ||
| return (entity < m_sparse.size() && m_sparse[entity] != INVALID_ENTITY); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::entityDestroyed(Entity entity) | ||
| { | ||
| if (hasComponent(entity)) | ||
| remove(entity); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::duplicateComponent(Entity sourceEntity, Entity destEntity) | ||
| { | ||
| if (!hasComponent(sourceEntity)) | ||
| THROW_EXCEPTION(ComponentNotFound, sourceEntity); | ||
|
|
||
| const void* sourceData = getRawComponent(sourceEntity); | ||
| insert(destEntity, sourceData); | ||
| } | ||
|
|
||
| size_t TypeErasedComponentArray::getComponentSize() const | ||
| { | ||
| return m_componentSize; | ||
| } | ||
|
|
||
| size_t TypeErasedComponentArray::size() const | ||
| { | ||
| return m_size; | ||
| } | ||
|
|
||
| void* TypeErasedComponentArray::getRawComponent(Entity entity) | ||
| { | ||
| if (!hasComponent(entity)) | ||
| return nullptr; | ||
| return m_componentData.data() + m_sparse[entity] * m_componentSize; | ||
| } | ||
|
|
||
| const void* TypeErasedComponentArray::getRawComponent(Entity entity) const | ||
| { | ||
| if (!hasComponent(entity)) | ||
| return nullptr; | ||
| return m_componentData.data() + m_sparse[entity] * m_componentSize; | ||
| } | ||
|
|
||
| void* TypeErasedComponentArray::getRawData() | ||
| { | ||
| return m_componentData.data(); | ||
| } | ||
|
|
||
| const void* TypeErasedComponentArray::getRawData() const | ||
| { | ||
| return m_componentData.data(); | ||
| } | ||
|
|
||
| std::span<const Entity> TypeErasedComponentArray::entities() const | ||
| { | ||
| return {m_dense.data(), m_size}; | ||
| } | ||
|
|
||
| Entity TypeErasedComponentArray::getEntityAtIndex(size_t index) const | ||
| { | ||
| if (index >= m_size) | ||
| THROW_EXCEPTION(OutOfRange, index); | ||
| return m_dense[index]; | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::addToGroup(Entity entity) | ||
| { | ||
| if (!hasComponent(entity)) | ||
| THROW_EXCEPTION(ComponentNotFound, entity); | ||
|
|
||
| size_t index = m_sparse[entity]; | ||
| if (index < m_groupSize) | ||
| return; | ||
|
|
||
| if (index != m_groupSize) { | ||
| swapComponents(index, m_groupSize); | ||
| std::swap(m_dense[index], m_dense[m_groupSize]); | ||
| m_sparse[m_dense[index]] = index; | ||
| m_sparse[m_dense[m_groupSize]] = m_groupSize; | ||
| } | ||
| ++m_groupSize; | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::removeFromGroup(Entity entity) | ||
| { | ||
| if (!hasComponent(entity)) | ||
| THROW_EXCEPTION(ComponentNotFound, entity); | ||
|
|
||
| size_t index = m_sparse[entity]; | ||
| if (index >= m_groupSize) | ||
| return; | ||
|
|
||
| --m_groupSize; | ||
| if (index != m_groupSize) { | ||
| swapComponents(index, m_groupSize); | ||
| std::swap(m_dense[index], m_dense[m_groupSize]); | ||
| m_sparse[m_dense[index]] = index; | ||
| m_sparse[m_dense[m_groupSize]] = m_groupSize; | ||
| } | ||
| } | ||
|
|
||
| constexpr size_t TypeErasedComponentArray::groupSize() const | ||
| { | ||
| return m_groupSize; | ||
| } | ||
|
|
||
| size_t TypeErasedComponentArray::memoryUsage() const | ||
| { | ||
| return m_componentData.capacity() | ||
| + sizeof(size_t) * m_sparse.capacity() | ||
| + sizeof(Entity) * m_dense.capacity(); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::ensureSparseCapacity(Entity entity) | ||
| { | ||
| if (entity >= m_sparse.size()) { | ||
| size_t newSize = m_sparse.size(); | ||
| if (newSize == 0) | ||
| newSize = m_capacity; | ||
| while (entity >= newSize) | ||
| newSize *= 2; | ||
| m_sparse.resize(newSize, INVALID_ENTITY); | ||
| } | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::swapComponents(const size_t index1, const size_t index2) | ||
| { | ||
| if (index1 == index2) return; | ||
|
|
||
| std::byte* data1 = m_componentData.data() + index1 * m_componentSize; | ||
| std::byte* data2 = m_componentData.data() + index2 * m_componentSize; | ||
|
|
||
| // Use a temporary buffer for swapping | ||
| std::vector<std::byte> temp(m_componentSize); | ||
| std::memcpy(temp.data(), data1, m_componentSize); | ||
| std::memcpy(data1, data2, m_componentSize); | ||
| std::memcpy(data2, temp.data(), m_componentSize); | ||
| } | ||
|
|
||
| void TypeErasedComponentArray::shrinkIfNeeded() | ||
| { | ||
| if (m_size < m_componentData.capacity() / 4 && m_componentData.capacity() > m_capacity * m_componentSize * 2) { | ||
| size_t newCapacity = std::max(m_size * 2, static_cast<size_t>(m_capacity)) * m_componentSize; | ||
| if (newCapacity < m_capacity * m_componentSize) | ||
| newCapacity = m_capacity * m_componentSize; | ||
|
|
||
| m_componentData.shrink_to_fit(); | ||
| m_dense.shrink_to_fit(); | ||
|
|
||
| m_componentData.reserve(newCapacity); | ||
| m_dense.reserve(newCapacity / m_componentSize); | ||
| } | ||
| } | ||
|
|
||
| } // namespace nexo::ecs | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.