Pico Headers
Loading...
Searching...
No Matches
pico_ecs.h
Go to the documentation of this file.
1
76#ifndef PICO_ECS_H
77#define PICO_ECS_H
78
79#include <stdbool.h> // bool, true, false
80#include <stddef.h> // size_t
81#include <stdint.h> // uint32_t
82
83#ifdef __cplusplus
84extern "C" {
85#endif
86
90typedef struct ecs_s ecs_t;
91
95typedef uint32_t ecs_id_t;
96
100#define ECS_NULL ((ecs_id_t)-1)
101
105typedef int8_t ecs_ret_t;
106
110#ifndef ECS_DT_TYPE
111#define ECS_DT_TYPE double
112#endif
113
115
124ecs_t* ecs_new(size_t entity_count, void* mem_ctx);
125
131void ecs_free(ecs_t* ecs);
132
136void ecs_reset(ecs_t* ecs);
137
145typedef void (*ecs_constructor_fn)(ecs_t* ecs,
146 ecs_id_t entity_id,
147 void* ptr,
148 void* args);
149
157typedef void (*ecs_destructor_fn)(ecs_t* ecs,
158 ecs_id_t entity_id,
159 void* ptr);
160
175 size_t size,
176 ecs_constructor_fn constructor,
177 ecs_destructor_fn destructor);
178
192 ecs_id_t* entities,
193 int entity_count,
194 ecs_dt_t dt,
195 void* udata);
196
204typedef void (*ecs_added_fn)(ecs_t* ecs, ecs_id_t entity_id, void* udata);
205
213typedef void (*ecs_removed_fn)(ecs_t* ecs, ecs_id_t entity_id, void* udata);
214
229 ecs_system_fn system_cb,
230 ecs_added_fn add_cb,
231 ecs_removed_fn remove_cb,
232 void* udata);
240void ecs_require_component(ecs_t* ecs, ecs_id_t sys_id, ecs_id_t comp_id);
241
250void ecs_exclude_component(ecs_t* ecs, ecs_id_t sys_id, ecs_id_t comp_id);
251
259
267
276
283bool ecs_is_ready(ecs_t* ecs, ecs_id_t entity_id);
284
293void ecs_destroy(ecs_t* ecs, ecs_id_t entity_id);
294
304bool ecs_has(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id);
305
315void* ecs_add(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id, void* args);
316
326void* ecs_get(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id);
327
335void ecs_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id);
336
345void ecs_queue_destroy(ecs_t* ecs, ecs_id_t entity_id);
346
357void ecs_queue_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id);
358
369
379
380#ifdef __cplusplus
381}
382#endif
383
384#endif // PICO_ECS_H
385
386#ifdef PICO_ECS_IMPLEMENTATION // Define once
387
388#include <stdint.h> // uint32_t
389#include <stdlib.h> // malloc, realloc, free
390#include <string.h> // memcpy, memset
391
392#ifndef PICO_ECS_MAX_COMPONENTS
393#define PICO_ECS_MAX_COMPONENTS 32
394#endif
395
396#ifndef PICO_ECS_MAX_SYSTEMS
397#define PICO_ECS_MAX_SYSTEMS 16
398#endif
399
400#ifdef NDEBUG
401 #define PICO_ECS_ASSERT(expr) ((void)0)
402#else
403 #ifndef PICO_ECS_ASSERT
404 #include <assert.h>
405 #define PICO_ECS_ASSERT(expr) (assert(expr))
406 #endif
407#endif
408
409#if !defined(PICO_ECS_MALLOC) || !defined(PICO_ECS_REALLOC) || !defined(PICO_ECS_FREE)
410#include <stdlib.h>
411#define PICO_ECS_MALLOC(size, ctx) (malloc(size))
412#define PICO_ECS_REALLOC(ptr, size, ctx) (realloc(ptr, size))
413#define PICO_ECS_FREE(ptr, ctx) (free(ptr))
414#endif
415
416/*=============================================================================
417 * Internal aliases
418 *============================================================================*/
419
420#define ECS_ASSERT PICO_ECS_ASSERT
421#define ECS_MAX_COMPONENTS PICO_ECS_MAX_COMPONENTS
422#define ECS_MAX_SYSTEMS PICO_ECS_MAX_SYSTEMS
423#define ECS_MALLOC PICO_ECS_MALLOC
424#define ECS_REALLOC PICO_ECS_REALLOC
425#define ECS_FREE PICO_ECS_FREE
426
427/*=============================================================================
428 * Internal data structures
429 *============================================================================*/
430
431#if ECS_MAX_COMPONENTS <= 32
432typedef uint32_t ecs_bitset_t;
433#elif ECS_MAX_COMPONENTS <= 64
434typedef uint64_t ecs_bitset_t;
435#else
436#define ECS_BITSET_WIDTH 64
437#define ECS_BITSET_SIZE (((ECS_MAX_COMPONENTS - 1) / ECS_BITSET_WIDTH) + 1)
438
439typedef struct
440{
441 uint64_t array[ECS_BITSET_SIZE];
442} ecs_bitset_t;
443#endif // ECS_MAX_COMPONENTS
444
445// Data-structure for a packed array implementation that provides O(1) functions
446// for adding, removing, and accessing entity IDs
447typedef struct
448{
449 size_t capacity;
450 size_t size;
451 size_t* sparse;
452 ecs_id_t* dense;
453} ecs_sparse_set_t;
454
455// Data-structure for an ID pool that provides O(1) operations for pooling IDs
456typedef struct
457{
458 size_t capacity;
459 size_t size;
460 ecs_id_t* array;
461} ecs_stack_t;
462
463typedef struct
464{
465 size_t capacity;
466 size_t count;
467 size_t size;
468 void* data;
469} ecs_array_t;
470
471typedef struct
472{
473 ecs_bitset_t comp_bits;
474 bool ready;
475} ecs_entity_t;
476
477typedef struct
478{
479 ecs_constructor_fn constructor;
480 ecs_destructor_fn destructor;
481} ecs_comp_t;
482
483typedef struct
484{
485 bool active;
486 ecs_sparse_set_t entity_ids;
487 ecs_system_fn system_cb;
488 ecs_added_fn add_cb;
489 ecs_removed_fn remove_cb;
490 ecs_bitset_t require_bits;
491 ecs_bitset_t exclude_bits;
492 void* udata;
493} ecs_sys_t;
494
495struct ecs_s
496{
497 ecs_stack_t entity_pool;
498 ecs_stack_t destroy_queue;
499 ecs_stack_t remove_queue;
500 ecs_entity_t* entities;
501 size_t entity_count;
502 ecs_comp_t comps[ECS_MAX_COMPONENTS];
503 ecs_array_t comp_arrays[ECS_MAX_COMPONENTS];
504 size_t comp_count;
505 ecs_sys_t systems[ECS_MAX_SYSTEMS];
506 size_t system_count;
507 void* mem_ctx;
508};
509
510/*=============================================================================
511 * Internal realloc wrapper
512 *============================================================================*/
513static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size);
514
515/*=============================================================================
516 * Calls destructors on all components of the entity
517 *============================================================================*/
518static void ecs_destruct(ecs_t* ecs, ecs_id_t entity_id);
519
520/*=============================================================================
521 * Internal functions to flush destroyed entities and removed component
522 *============================================================================*/
523static void ecs_flush_destroyed(ecs_t* ecs);
524static void ecs_flush_removed(ecs_t* ecs);
525
526/*=============================================================================
527 * Internal bit set functions
528 *============================================================================*/
529static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on);
530static inline bool ecs_bitset_is_zero(ecs_bitset_t* set);
531static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit);
532static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2);
533static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2);
534static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set);
535static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2);
536static inline bool ecs_bitset_true(ecs_bitset_t* set);
537
538/*=============================================================================
539 * Internal sparse set functions
540 *============================================================================*/
541static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity);
542static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set);
543static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id);
544static size_t ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id);
545static bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id);
546
547/*=============================================================================
548 * Internal system entity add/remove functions
549 *============================================================================*/
550static bool ecs_entity_system_test(ecs_bitset_t* require_bits,
551 ecs_bitset_t* exclude_bits,
552 ecs_bitset_t* entity_bits);
553
554/*=============================================================================
555 * Internal ID pool functions
556 *============================================================================*/
557static void ecs_stack_init(ecs_t* ecs, ecs_stack_t* pool, int capacity);
558static void ecs_stack_free(ecs_t* ecs, ecs_stack_t* pool);
559static void ecs_stack_push(ecs_t* ecs, ecs_stack_t* pool, ecs_id_t id);
560static ecs_id_t ecs_stack_pop(ecs_stack_t* pool);
561static int ecs_stack_size(ecs_stack_t* pool);
562
563/*=============================================================================
564 * Internal array functions
565 *============================================================================*/
566static void ecs_array_init(ecs_t* ecs, ecs_array_t* array, size_t size, size_t capacity);
567static void ecs_array_free(ecs_t* ecs, ecs_array_t* array);
568static void ecs_array_resize(ecs_t* ecs, ecs_array_t* array, size_t capacity);
569
570/*=============================================================================
571 * Internal validation functions
572 *============================================================================*/
573#ifndef NDEBUG
574static bool ecs_is_not_null(void* ptr);
575static bool ecs_is_valid_component_id(ecs_id_t id);
576static bool ecs_is_valid_system_id(ecs_id_t id);
577static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id);
578static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id);
579static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id);
580#endif // NDEBUG
581/*=============================================================================
582 * Public API implementation
583 *============================================================================*/
584
585ecs_t* ecs_new(size_t entity_count, void* mem_ctx)
586{
587 ECS_ASSERT(entity_count > 0);
588
589 ecs_t* ecs = (ecs_t*)ECS_MALLOC(sizeof(ecs_t), mem_ctx);
590
591 // Out of memory
592 if (NULL == ecs)
593 return NULL;
594
595 memset(ecs, 0, sizeof(ecs_t));
596
597 ecs->entity_count = entity_count;
598 ecs->mem_ctx = mem_ctx;
599
600 // Initialize entity pool and queues
601 ecs_stack_init(ecs, &ecs->entity_pool, entity_count);
602 ecs_stack_init(ecs, &ecs->destroy_queue, entity_count);
603 ecs_stack_init(ecs, &ecs->remove_queue, entity_count * 2);
604
605 // Allocate entity array
606 ecs->entities = (ecs_entity_t*)ECS_MALLOC(ecs->entity_count * sizeof(ecs_entity_t),
607 ecs->mem_ctx);
608
609 // Zero entity array
610 memset(ecs->entities, 0, ecs->entity_count * sizeof(ecs_entity_t));
611
612 // Pre-populate the the ID pool
613 for (ecs_id_t id = 0; id < entity_count; id++)
614 {
615 ecs_stack_push(ecs, &ecs->entity_pool, id);
616 }
617
618 return ecs;
619}
620
621void ecs_free(ecs_t* ecs)
622{
623 ECS_ASSERT(ecs_is_not_null(ecs));
624
625 for (ecs_id_t entity_id = 0; entity_id < ecs->entity_count; entity_id++)
626 {
627 if (ecs->entities[entity_id].ready)
628 ecs_destruct(ecs, entity_id);
629 }
630
631 ecs_stack_free(ecs, &ecs->entity_pool);
632 ecs_stack_free(ecs, &ecs->destroy_queue);
633 ecs_stack_free(ecs, &ecs->remove_queue);
634
635 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
636 {
637 ecs_array_t* comp_array = &ecs->comp_arrays[comp_id];
638 ecs_array_free(ecs, comp_array);
639 }
640
641 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
642 {
643 ecs_sys_t* sys = &ecs->systems[sys_id];
644 ecs_sparse_set_free(ecs, &sys->entity_ids);
645 }
646
647 ECS_FREE(ecs->entities, ecs->mem_ctx);
648 ECS_FREE(ecs, ecs->mem_ctx);
649}
650
651void ecs_reset(ecs_t* ecs)
652{
653 ECS_ASSERT(ecs_is_not_null(ecs));
654
655 for (ecs_id_t entity_id = 0; entity_id < ecs->entity_count; entity_id++)
656 {
657 if (ecs->entities[entity_id].ready)
658 ecs_destruct(ecs, entity_id);
659 }
660
661 ecs->entity_pool.size = 0;
662 ecs->destroy_queue.size = 0;
663 ecs->remove_queue.size = 0;
664
665 memset(ecs->entities, 0, ecs->entity_count * sizeof(ecs_entity_t));
666
667 for (ecs_id_t entity_id = 0; entity_id < ecs->entity_count; entity_id++)
668 {
669 ecs_stack_push(ecs, &ecs->entity_pool, entity_id);
670 }
671
672 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
673 {
674 ecs->systems[sys_id].entity_ids.size = 0;
675 }
676}
677
679 size_t size,
680 ecs_constructor_fn constructor,
681 ecs_destructor_fn destructor)
682{
683 ECS_ASSERT(ecs_is_not_null(ecs));
684 ECS_ASSERT(ecs->comp_count < ECS_MAX_COMPONENTS);
685 ECS_ASSERT(size > 0);
686
687 ecs_id_t comp_id = ecs->comp_count;
688
689 ecs_array_t* comp_array = &ecs->comp_arrays[comp_id];
690 ecs_array_init(ecs, comp_array, size, ecs->entity_count);
691
692 ecs->comps[comp_id].constructor = constructor;
693 ecs->comps[comp_id].destructor = destructor;
694
695 ecs->comp_count++;
696
697 return comp_id;
698}
699
701 ecs_system_fn system_cb,
702 ecs_added_fn add_cb,
703 ecs_removed_fn remove_cb,
704 void* udata)
705{
706 ECS_ASSERT(ecs_is_not_null(ecs));
707 ECS_ASSERT(ecs->system_count < ECS_MAX_SYSTEMS);
708 ECS_ASSERT(NULL != system_cb);
709
710 ecs_id_t sys_id = ecs->system_count;
711 ecs_sys_t* sys = &ecs->systems[sys_id];
712
713 ecs_sparse_set_init(ecs, &sys->entity_ids, ecs->entity_count);
714
715 sys->active = true;
716 sys->system_cb = system_cb;
717 sys->add_cb = add_cb;
718 sys->remove_cb = remove_cb;
719 sys->udata = udata;
720
721 ecs->system_count++;
722
723 return sys_id;
724}
725
726void ecs_require_component(ecs_t* ecs, ecs_id_t sys_id, ecs_id_t comp_id)
727{
728 ECS_ASSERT(ecs_is_not_null(ecs));
729 ECS_ASSERT(ecs_is_valid_system_id(sys_id));
730 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
731 ECS_ASSERT(ecs_is_system_ready(ecs, sys_id));
732 ECS_ASSERT(ecs_is_component_ready(ecs, comp_id));
733
734 // Set system component bit for the specified component
735 ecs_sys_t* sys = &ecs->systems[sys_id];
736 ecs_bitset_flip(&sys->require_bits, comp_id, true);
737}
738
739void ecs_exclude_component(ecs_t* ecs, ecs_id_t sys_id, ecs_id_t comp_id)
740{
741 ECS_ASSERT(ecs_is_not_null(ecs));
742 ECS_ASSERT(ecs_is_valid_system_id(sys_id));
743 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
744 ECS_ASSERT(ecs_is_system_ready(ecs, sys_id));
745 ECS_ASSERT(ecs_is_component_ready(ecs, comp_id));
746
747 // Set system component bit for the specified component
748 ecs_sys_t* sys = &ecs->systems[sys_id];
749 ecs_bitset_flip(&sys->exclude_bits, comp_id, true);
750}
751
752void ecs_enable_system(ecs_t* ecs, ecs_id_t sys_id)
753{
754 ECS_ASSERT(ecs_is_not_null(ecs));
755 ECS_ASSERT(ecs_is_valid_system_id(sys_id));
756 ECS_ASSERT(ecs_is_system_ready(ecs, sys_id));
757
758 ecs_sys_t* sys = &ecs->systems[sys_id];
759 sys->active = true;
760}
761
762void ecs_disable_system(ecs_t* ecs, ecs_id_t sys_id)
763{
764 ECS_ASSERT(ecs_is_not_null(ecs));
765 ECS_ASSERT(ecs_is_valid_system_id(sys_id));
766 ECS_ASSERT(ecs_is_system_ready(ecs, sys_id));
767
768 ecs_sys_t* sys = &ecs->systems[sys_id];
769 sys->active = false;
770}
771
773{
774 ECS_ASSERT(ecs_is_not_null(ecs));
775
776 ecs_stack_t* pool = &ecs->entity_pool;
777
778 // If pool is empty, increase the number of entity IDs
779 if (0 == ecs_stack_size(pool))
780 {
781 size_t old_count = ecs->entity_count;
782 size_t new_count = old_count + (old_count / 2) + 2;
783
784 // Reallocates entities and zeros new ones
785 ecs->entities = (ecs_entity_t*)ecs_realloc_zero(ecs, ecs->entities,
786 old_count * sizeof(ecs_entity_t),
787 new_count * sizeof(ecs_entity_t));
788
789 // Push new entity IDs into the pool
790 for (ecs_id_t id = old_count; id < new_count; id++)
791 {
792 ecs_stack_push(ecs, pool, id);
793 }
794
795 // Update entity count
796 ecs->entity_count = new_count;
797 }
798
799 ecs_id_t entity_id = ecs_stack_pop(pool);
800 ecs->entities[entity_id].ready = true;
801
802 return entity_id;
803}
804
805bool ecs_is_ready(ecs_t* ecs, ecs_id_t entity_id)
806{
807 ECS_ASSERT(ecs_is_not_null(ecs));
808
809 return ecs->entities[entity_id].ready;
810}
811
812void ecs_destroy(ecs_t* ecs, ecs_id_t entity_id)
813{
814 ECS_ASSERT(ecs_is_not_null(ecs));
815 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
816
817 // Load entity
818 ecs_entity_t* entity = &ecs->entities[entity_id];
819
820 // Remove entity from systems
821 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
822 {
823 ecs_sys_t* sys = &ecs->systems[sys_id];
824
825 if (ecs_entity_system_test(&sys->require_bits, &sys->exclude_bits, &entity->comp_bits) &&
826 ecs_sparse_set_remove(&sys->entity_ids, entity_id))
827 {
828 if (sys->remove_cb)
829 sys->remove_cb(ecs, entity_id, sys->udata);
830 }
831 }
832
833 // Call destructors on entity components
834 ecs_destruct(ecs, entity_id);
835
836 // Push entity ID back into pool
837 ecs_stack_t* pool = &ecs->entity_pool;
838 ecs_stack_push(ecs, pool, entity_id);
839
840 // Reset entity (sets bitset to 0 and ready to false)
841 memset(entity, 0, sizeof(ecs_entity_t));
842}
843
844bool ecs_has(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id)
845{
846 ECS_ASSERT(ecs_is_not_null(ecs));
847 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
848 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
849
850 // Load entity
851 ecs_entity_t* entity = &ecs->entities[entity_id];
852
853 // Return true if the component belongs to the entity
854 return ecs_bitset_test(&entity->comp_bits, comp_id);
855}
856
857void* ecs_get(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id)
858{
859 ECS_ASSERT(ecs_is_not_null(ecs));
860 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
861 ECS_ASSERT(ecs_is_component_ready(ecs, comp_id));
862 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
863
864 // Return pointer to component
865 // eid0, eid1 eid2, ...
866 // [comp0, comp1, comp2, ...]
867 ecs_array_t* comp_array = &ecs->comp_arrays[comp_id];
868 return (char*)comp_array->data + (comp_array->size * entity_id);
869}
870
871void* ecs_add(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id, void* args)
872{
873 ECS_ASSERT(ecs_is_not_null(ecs));
874 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
875 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
876 ECS_ASSERT(ecs_is_component_ready(ecs, comp_id));
877
878 // Load entity
879 ecs_entity_t* entity = &ecs->entities[entity_id];
880
881 // Load component
882 ecs_array_t* comp_array = &ecs->comp_arrays[comp_id];
883 ecs_comp_t* comp = &ecs->comps[comp_id];
884
885 // Grow the component array
886 ecs_array_resize(ecs, comp_array, entity_id);
887
888 // Get pointer to component
889 void* ptr = ecs_get(ecs, entity_id, comp_id);
890
891 // Zero component
892 memset(ptr, 0, comp_array->size);
893
894 // Call constructor
895 if (comp->constructor)
896 comp->constructor(ecs, entity_id, ptr, args);
897
898 // Set entity component bit that determines which systems this entity
899 // belongs to
900 ecs_bitset_flip(&entity->comp_bits, comp_id, true);
901
902 // Add or remove entity from systems
903 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
904 {
905 ecs_sys_t* sys = &ecs->systems[sys_id];
906
907 if (ecs_entity_system_test(&sys->require_bits, &sys->exclude_bits, &entity->comp_bits))
908 {
909 if (ecs_sparse_set_add(ecs, &sys->entity_ids, entity_id))
910 {
911 if (sys->add_cb)
912 sys->add_cb(ecs, entity_id, sys->udata);
913 }
914 }
915 else // Just remove the entity if its components no longer match for whatever reason.
916 {
917 if (!ecs_bitset_is_zero(&sys->exclude_bits) &&
918 ecs_sparse_set_remove(&sys->entity_ids, entity_id))
919 {
920 if (sys->remove_cb)
921 sys->remove_cb(ecs, entity_id, sys->udata);
922 }
923 }
924 }
925
926 // Return component
927 return ptr;
928}
929
930void ecs_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id)
931{
932 ECS_ASSERT(ecs_is_not_null(ecs));
933 ECS_ASSERT(ecs_is_valid_component_id(comp_id));
934 ECS_ASSERT(ecs_is_component_ready(ecs, comp_id));
935 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
936
937 // Load entity
938 ecs_entity_t* entity = &ecs->entities[entity_id];
939
940 // Create bit mask with comp bit flipped on
941 ecs_bitset_t comp_bit;
942
943 memset(&comp_bit, 0, sizeof(ecs_bitset_t));
944 ecs_bitset_flip(&comp_bit, comp_id, true);
945
946 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
947 {
948 ecs_sys_t* sys = &ecs->systems[sys_id];
949
950 if (ecs_entity_system_test(&sys->require_bits, &sys->exclude_bits, &comp_bit))
951 {
952 if (ecs_sparse_set_remove(&sys->entity_ids, entity_id))
953 {
954 if (sys->remove_cb)
955 sys->remove_cb(ecs, entity_id, sys->udata);
956 }
957 }
958 else
959 {
960 if (!ecs_bitset_is_zero(&sys->exclude_bits) &&
961 ecs_sparse_set_add(ecs, &sys->entity_ids, entity_id))
962 {
963 if (sys->add_cb)
964 sys->add_cb(ecs, entity_id, sys->udata);
965 }
966 }
967 }
968
969 ecs_comp_t* comp = &ecs->comps[comp_id];
970
971 if (comp->destructor)
972 {
973 void* ptr = ecs_get(ecs, entity_id, comp_id);
974 comp->destructor(ecs, entity_id, ptr);
975 }
976
977 // Reset the relevant component mask bit
978 ecs_bitset_flip(&entity->comp_bits, comp_id, false);
979}
980
981void ecs_queue_destroy(ecs_t* ecs, ecs_id_t entity_id)
982{
983 ECS_ASSERT(ecs_is_not_null(ecs));
984 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
985
986 ecs_stack_push(ecs, &ecs->destroy_queue, entity_id);
987}
988
989void ecs_queue_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id)
990{
991 ECS_ASSERT(ecs_is_not_null(ecs));
992 ECS_ASSERT(ecs_is_entity_ready(ecs, entity_id));
993 ECS_ASSERT(ecs_has(ecs, entity_id, comp_id));
994
995 ecs_stack_push(ecs, &ecs->remove_queue, entity_id);
996 ecs_stack_push(ecs, &ecs->remove_queue, comp_id);
997}
998
1000{
1001 ECS_ASSERT(ecs_is_not_null(ecs));
1002 ECS_ASSERT(ecs_is_valid_system_id(sys_id));
1003 ECS_ASSERT(ecs_is_system_ready(ecs, sys_id));
1004 ECS_ASSERT(dt >= 0.0f);
1005
1006 ecs_sys_t* sys = &ecs->systems[sys_id];
1007
1008 if (!sys->active)
1009 return 0;
1010
1011 ecs_ret_t code = sys->system_cb(ecs,
1012 sys->entity_ids.dense,
1013 sys->entity_ids.size,
1014 dt,
1015 sys->udata);
1016
1017 ecs_flush_destroyed(ecs);
1018 ecs_flush_removed(ecs);
1019
1020 return code;
1021}
1022
1024{
1025 ECS_ASSERT(ecs_is_not_null(ecs));
1026 ECS_ASSERT(dt >= 0.0f);
1027
1028 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1029 {
1030 ecs_ret_t code = ecs_update_system(ecs, sys_id, dt);
1031
1032 if (0 != code)
1033 return code;
1034 }
1035
1036 return 0;
1037}
1038
1039/*=============================================================================
1040 * Internal realloc wrapper
1041 *============================================================================*/
1042static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size)
1043{
1044 (void)ecs;
1045
1046 ptr = ECS_REALLOC(ptr, new_size, ecs->mem_ctx);
1047
1048 if (new_size > old_size && ptr) {
1049 size_t diff = new_size - old_size;
1050 void* start = ((char*)ptr)+ old_size;
1051 memset(start, 0, diff);
1052 }
1053
1054 return ptr;
1055}
1056
1057/*=============================================================================
1058 * Calls destructors on all components of the entity
1059 *============================================================================*/
1060static void ecs_destruct(ecs_t* ecs, ecs_id_t entity_id)
1061{
1062 // Load entity
1063 ecs_entity_t* entity = &ecs->entities[entity_id];
1064
1065 // Loop through components and call the destructors
1066 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
1067 {
1068 if (ecs_bitset_test(&entity->comp_bits, comp_id))
1069 {
1070 ecs_comp_t* comp = &ecs->comps[comp_id];
1071
1072 if (comp->destructor)
1073 {
1074 void* ptr = ecs_get(ecs, entity_id, comp_id);
1075 comp->destructor(ecs, entity_id, ptr);
1076 }
1077 }
1078 }
1079}
1080
1081/*=============================================================================
1082 * Internal functions to flush destroyed entity and removed component
1083 *============================================================================*/
1084
1085static void ecs_flush_destroyed(ecs_t* ecs)
1086{
1087 ecs_stack_t* destroy_queue = &ecs->destroy_queue;
1088
1089 for (size_t i = 0; i < destroy_queue->size; i++)
1090 {
1091 ecs_id_t entity_id = destroy_queue->array[i];
1092
1093 if (ecs_is_ready(ecs, entity_id))
1094 ecs_destroy(ecs, entity_id);
1095 }
1096
1097 destroy_queue->size = 0;
1098}
1099
1100static void ecs_flush_removed(ecs_t* ecs)
1101{
1102 ecs_stack_t* remove_queue = &ecs->remove_queue;
1103
1104 for (size_t i = 0; i < remove_queue->size; i += 2)
1105 {
1106 ecs_id_t entity_id = remove_queue->array[i];
1107
1108 if (ecs_is_ready(ecs, entity_id))
1109 {
1110 ecs_id_t comp_id = remove_queue->array[i + 1];
1111 ecs_remove(ecs, entity_id, comp_id);
1112 }
1113 }
1114
1115 remove_queue->size = 0;
1116}
1117
1118
1119/*=============================================================================
1120 * Internal bitset functions
1121 *============================================================================*/
1122
1123#if ECS_MAX_COMPONENTS <= 64
1124
1125static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1126{
1127 return *set == 0;
1128}
1129
1130static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1131{
1132 if (on)
1133 *set |= ((uint64_t)1 << bit);
1134 else
1135 *set &= ~((uint64_t)1 << bit);
1136}
1137
1138static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1139{
1140 return *set & ((uint64_t)1 << bit);
1141}
1142
1143static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2)
1144{
1145 return *set1 & *set2;
1146}
1147
1148static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2)
1149{
1150 return *set1 | *set2;
1151}
1152
1153static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1154{
1155 return ~(*set);
1156}
1157
1158static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1159{
1160 return *set1 == *set2;
1161}
1162
1163static inline bool ecs_bitset_true(ecs_bitset_t* set)
1164{
1165 return *set;
1166}
1167
1168#else // ECS_MAX_COMPONENTS
1169
1170static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1171{
1172 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1173 {
1174 if (set->array[i] != 0)
1175 return false;
1176 }
1177
1178 return true;
1179}
1180
1181static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1182{
1183 int index = bit / ECS_BITSET_WIDTH;
1184
1185 if (on)
1186 set->array[index] |= ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1187 else
1188 set->array[index] &= ~((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1189}
1190
1191static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1192{
1193 int index = bit / ECS_BITSET_WIDTH;
1194 return set->array[index] & ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1195}
1196
1197static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1,
1198 ecs_bitset_t* set2)
1199{
1200 ecs_bitset_t set;
1201
1202 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1203 {
1204 set.array[i] = set1->array[i] & set2->array[i];
1205 }
1206
1207 return set;
1208}
1209
1210static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1,
1211 ecs_bitset_t* set2)
1212{
1213 ecs_bitset_t set;
1214
1215 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1216 {
1217 set.array[i] = set1->array[i] | set2->array[i];
1218 }
1219
1220 return set;
1221}
1222
1223
1224static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1225{
1226 ecs_bitset_t out;
1227
1228 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1229 {
1230 out.array[i] = ~set->array[i];
1231 }
1232
1233 return out;
1234}
1235
1236static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1237{
1238 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1239 {
1240 if (set1->array[i] != set2->array[i])
1241 {
1242 return false;
1243 }
1244 }
1245
1246 return true;
1247}
1248
1249static inline bool ecs_bitset_true(ecs_bitset_t* set)
1250{
1251 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1252 {
1253 if (set->array[i])
1254 return true;
1255 }
1256
1257 return false;
1258}
1259
1260#endif // ECS_MAX_COMPONENTS
1261
1262/*=============================================================================
1263 * Internal sparse set functions
1264 *============================================================================*/
1265
1266static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity)
1267{
1268 ECS_ASSERT(ecs_is_not_null(ecs));
1269 ECS_ASSERT(ecs_is_not_null(set));
1270 ECS_ASSERT(capacity > 0);
1271
1272 (void)ecs;
1273
1274 set->capacity = capacity;
1275 set->size = 0;
1276
1277 set->dense = (ecs_id_t*)ECS_MALLOC(capacity * sizeof(ecs_id_t), ecs->mem_ctx);
1278 set->sparse = (size_t*) ECS_MALLOC(capacity * sizeof(size_t), ecs->mem_ctx);
1279
1280 memset(set->sparse, 0, capacity * sizeof(size_t));
1281}
1282
1283static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set)
1284{
1285 ECS_ASSERT(ecs_is_not_null(ecs));
1286 ECS_ASSERT(ecs_is_not_null(set));
1287
1288 (void)ecs;
1289
1290 ECS_FREE(set->dense, ecs->mem_ctx);
1291 ECS_FREE(set->sparse, ecs->mem_ctx);
1292}
1293
1294static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id)
1295{
1296 ECS_ASSERT(ecs_is_not_null(ecs));
1297 ECS_ASSERT(ecs_is_not_null(set));
1298
1299 (void)ecs;
1300
1301 // Grow sparse set if necessary
1302 if (id >= set->capacity)
1303 {
1304 size_t old_capacity = set->capacity;
1305 size_t new_capacity = old_capacity;
1306
1307 // Calculate new capacity
1308 while (new_capacity <= id)
1309 {
1310 new_capacity += (new_capacity / 2) + 2;
1311 }
1312
1313 // Grow dense array
1314 set->dense = (ecs_id_t*)ECS_REALLOC(set->dense,
1315 new_capacity * sizeof(ecs_id_t),
1316 ecs->mem_ctx);
1317
1318 // Grow sparse array and zero it
1319 set->sparse = (size_t*)ecs_realloc_zero(ecs,
1320 set->sparse,
1321 old_capacity * sizeof(size_t),
1322 new_capacity * sizeof(size_t));
1323
1324 // Set the new capacity
1325 set->capacity = new_capacity;
1326 }
1327
1328 // Check if ID exists within the set
1329 if (ECS_NULL != ecs_sparse_set_find(set, id))
1330 return false;
1331
1332 // Add ID to set
1333 set->dense[set->size] = id;
1334 set->sparse[id] = set->size;
1335
1336 set->size++;
1337
1338 return true;
1339}
1340
1341static size_t ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id)
1342{
1343 ECS_ASSERT(ecs_is_not_null(set));
1344
1345 if (set->sparse[id] < set->size && set->dense[set->sparse[id]] == id)
1346 return set->sparse[id];
1347 else
1348 return ECS_NULL;
1349}
1350
1351static bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id)
1352{
1353 ECS_ASSERT(ecs_is_not_null(set));
1354
1355 if (ECS_NULL == ecs_sparse_set_find(set, id))
1356 return false;
1357
1358 // Swap and remove (changes order of array)
1359 ecs_id_t tmp = set->dense[set->size - 1];
1360 set->dense[set->sparse[id]] = tmp;
1361 set->sparse[tmp] = set->sparse[id];
1362
1363 set->size--;
1364
1365 return true;
1366}
1367
1368/*=============================================================================
1369 * Internal system entity add/remove functions
1370 *============================================================================*/
1371
1372inline static bool ecs_entity_system_test(ecs_bitset_t* require_bits,
1373 ecs_bitset_t* exclude_bits,
1374 ecs_bitset_t* entity_bits)
1375{
1376 if (!ecs_bitset_is_zero(exclude_bits))
1377 {
1378 ecs_bitset_t overlap = ecs_bitset_and(entity_bits, exclude_bits);
1379
1380 if (ecs_bitset_true(&overlap))
1381 {
1382 return false;
1383 }
1384 }
1385
1386 ecs_bitset_t entity_and_require = ecs_bitset_and(entity_bits, require_bits);
1387 return ecs_bitset_equal(&entity_and_require, require_bits);
1388}
1389
1390/*=============================================================================
1391 * Internal ID pool functions
1392 *============================================================================*/
1393
1394inline static void ecs_stack_init(ecs_t* ecs, ecs_stack_t* stack, int capacity)
1395{
1396 ECS_ASSERT(ecs_is_not_null(ecs));
1397 ECS_ASSERT(ecs_is_not_null(stack));
1398 ECS_ASSERT(capacity > 0);
1399
1400 (void)ecs;
1401
1402 stack->size = 0;
1403 stack->capacity = capacity;
1404 stack->array = (ecs_id_t*)ECS_MALLOC(capacity * sizeof(ecs_id_t), ecs->mem_ctx);
1405}
1406
1407inline static void ecs_stack_free(ecs_t* ecs, ecs_stack_t* stack)
1408{
1409 ECS_ASSERT(ecs_is_not_null(ecs));
1410 ECS_ASSERT(ecs_is_not_null(stack));
1411
1412 (void)ecs;
1413
1414 ECS_FREE(stack->array, ecs->mem_ctx);
1415}
1416
1417inline static void ecs_stack_push(ecs_t* ecs, ecs_stack_t* stack, ecs_id_t id)
1418{
1419 ECS_ASSERT(ecs_is_not_null(ecs));
1420 ECS_ASSERT(ecs_is_not_null(stack));
1421 ECS_ASSERT(stack->capacity > 0);
1422
1423 (void)ecs;
1424
1425 if (stack->size == stack->capacity)
1426 {
1427 stack->capacity += (stack->capacity / 2) + 2;
1428
1429 stack->array = (ecs_id_t*)ECS_REALLOC(stack->array,
1430 stack->capacity * sizeof(ecs_id_t),
1431 ecs->mem_ctx);
1432 }
1433
1434 stack->array[stack->size++] = id;
1435}
1436
1437inline static ecs_id_t ecs_stack_pop(ecs_stack_t* stack)
1438{
1439 ECS_ASSERT(ecs_is_not_null(stack));
1440 return stack->array[--stack->size];
1441}
1442
1443inline static int ecs_stack_size(ecs_stack_t* stack)
1444{
1445 return stack->size;
1446}
1447
1448static void ecs_array_init(ecs_t* ecs, ecs_array_t* array, size_t size, size_t capacity)
1449{
1450 ECS_ASSERT(ecs_is_not_null(ecs));
1451 ECS_ASSERT(ecs_is_not_null(array));
1452
1453 (void)ecs;
1454
1455 memset(array, 0, sizeof(ecs_array_t));
1456
1457 array->capacity = capacity;
1458 array->count = 0;
1459 array->size = size;
1460 array->data = ECS_MALLOC(size * capacity, ecs->mem_ctx);
1461}
1462
1463static void ecs_array_free(ecs_t* ecs, ecs_array_t* array)
1464{
1465 ECS_ASSERT(ecs_is_not_null(ecs));
1466 ECS_ASSERT(ecs_is_not_null(array));
1467
1468 (void)ecs;
1469
1470 ECS_FREE(array->data, ecs->mem_ctx);
1471}
1472
1473static void ecs_array_resize(ecs_t* ecs, ecs_array_t* array, size_t capacity)
1474{
1475 ECS_ASSERT(ecs_is_not_null(ecs));
1476 ECS_ASSERT(ecs_is_not_null(array));
1477
1478 (void)ecs;
1479
1480 if (capacity >= array->capacity)
1481 {
1482 while (array->capacity <= capacity)
1483 {
1484 array->capacity += (array->capacity / 2) + 2;
1485 }
1486
1487 array->data = ECS_REALLOC(array->data,
1488 array->capacity * array->size,
1489 ecs->mem_ctx);
1490 }
1491}
1492
1493/*=============================================================================
1494 * Internal validation functions
1495 *============================================================================*/
1496#ifndef NDEBUG
1497static bool ecs_is_not_null(void* ptr)
1498{
1499 return NULL != ptr;
1500}
1501
1502static bool ecs_is_valid_component_id(ecs_id_t id)
1503{
1504 return id < ECS_MAX_COMPONENTS;
1505}
1506
1507static bool ecs_is_valid_system_id(ecs_id_t id)
1508{
1509 return id < ECS_MAX_SYSTEMS;
1510}
1511
1512static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id)
1513{
1514 return ecs->entities[entity_id].ready;
1515}
1516
1517static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id)
1518{
1519 return comp_id < ecs->comp_count;
1520}
1521
1522static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id)
1523{
1524 return sys_id < ecs->system_count;
1525}
1526
1527#endif // NDEBUG
1528
1529#endif // PICO_ECS_IMPLEMENTATION
1530
1531/*
1532 ----------------------------------------------------------------------------
1533 This software is available under two licenses (A) or (B). You may choose
1534 either one as you wish:
1535 ----------------------------------------------------------------------------
1536
1537 (A) The zlib License
1538
1539 Copyright (c) 2021 James McLean
1540
1541 This software is provided 'as-is', without any express or implied warranty.
1542 In no event will the authors be held liable for any damages arising from the
1543 use of this software.
1544
1545 Permission is granted to anyone to use this software for any purpose,
1546 including commercial applications, and to alter it and redistribute it
1547 freely, subject to the following restrictions:
1548
1549 1. The origin of this software must not be misrepresented; you must not
1550 claim that you wrote the original software. If you use this software in a
1551 product, an acknowledgment in the product documentation would be appreciated
1552 but is not required.
1553
1554 2. Altered source versions must be plainly marked as such, and must not be
1555 misrepresented as being the original software.
1556
1557 3. This notice may not be removed or altered from any source distribution.
1558
1559 ----------------------------------------------------------------------------
1560
1561 (B) Public Domain (www.unlicense.org)
1562
1563 This is free and unencumbered software released into the public domain.
1564
1565 Anyone is free to copy, modify, publish, use, compile, sell, or distribute
1566 this software, either in source code form or as a compiled binary, for any
1567 purpose, commercial or non-commercial, and by any means.
1568
1569 In jurisdictions that recognize copyright laws, the author or authors of
1570 this software dedicate any and all copyright interest in the software to the
1571 public domain. We make this dedication for the benefit of the public at
1572 large and to the detriment of our heirs and successors. We intend this
1573 dedication to be an overt act of relinquishment in perpetuity of all present
1574 and future rights to this software under copyright law.
1575
1576 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1577 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1578 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1579 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1580 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1581 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1582*/
1583
1584// EoF
void ecs_disable_system(ecs_t *ecs, ecs_id_t sys_id)
Disables a system.
void ecs_queue_remove(ecs_t *ecs, ecs_id_t entity_id, ecs_id_t comp_id)
Queues a component for removable.
void(* ecs_removed_fn)(ecs_t *ecs, ecs_id_t entity_id, void *udata)
Called when an entity is removed from a system.
Definition pico_ecs.h:213
void(* ecs_destructor_fn)(ecs_t *ecs, ecs_id_t entity_id, void *ptr)
Called when a component is destroyed (via ecs_remove or ecs_destroy)
Definition pico_ecs.h:157
ECS_DT_TYPE ecs_dt_t
Definition pico_ecs.h:114
ecs_id_t ecs_register_system(ecs_t *ecs, ecs_system_fn system_cb, ecs_added_fn add_cb, ecs_removed_fn remove_cb, void *udata)
Registers a system.
int8_t ecs_ret_t
Return code for update callback and calling functions.
Definition pico_ecs.h:105
ecs_id_t ecs_register_component(ecs_t *ecs, size_t size, ecs_constructor_fn constructor, ecs_destructor_fn destructor)
Registers a component.
void ecs_free(ecs_t *ecs)
Destroys an ECS instance.
#define ECS_DT_TYPE
Determine delta-time type.
Definition pico_ecs.h:111
void ecs_exclude_component(ecs_t *ecs, ecs_id_t sys_id, ecs_id_t comp_id)
Excludes entities having the specified component from being added to the target system.
void(* ecs_added_fn)(ecs_t *ecs, ecs_id_t entity_id, void *udata)
Called when an entity is added to a system.
Definition pico_ecs.h:204
#define ECS_NULL
NULL/invalid/undefined value.
Definition pico_ecs.h:100
ecs_id_t ecs_create(ecs_t *ecs)
Creates an entity.
void(* ecs_constructor_fn)(ecs_t *ecs, ecs_id_t entity_id, void *ptr, void *args)
Called when a component is created (via ecs_add)
Definition pico_ecs.h:145
struct ecs_s ecs_t
ECS context.
Definition pico_ecs.h:90
void * ecs_add(ecs_t *ecs, ecs_id_t entity_id, ecs_id_t comp_id, void *args)
Adds a component instance to an entity.
void ecs_reset(ecs_t *ecs)
Removes all entities from the ECS, preserving systems and components.
void ecs_destroy(ecs_t *ecs, ecs_id_t entity_id)
Destroys an entity.
ecs_ret_t ecs_update_systems(ecs_t *ecs, ecs_dt_t dt)
Updates all systems.
void ecs_queue_destroy(ecs_t *ecs, ecs_id_t entity_id)
Queues an entity for destruction at the end of system execution.
void ecs_remove(ecs_t *ecs, ecs_id_t entity_id, ecs_id_t comp_id)
Removes a component instance from an entity.
ecs_ret_t(* ecs_system_fn)(ecs_t *ecs, ecs_id_t *entities, int entity_count, ecs_dt_t dt, void *udata)
System update callback.
Definition pico_ecs.h:191
uint32_t ecs_id_t
ID used for entity and components.
Definition pico_ecs.h:95
bool ecs_is_ready(ecs_t *ecs, ecs_id_t entity_id)
Returns true if the entity is currently active.
void ecs_enable_system(ecs_t *ecs, ecs_id_t sys_id)
Enables a system.
void ecs_require_component(ecs_t *ecs, ecs_id_t sys_id, ecs_id_t comp_id)
Determines which components are available to the specified system.
ecs_ret_t ecs_update_system(ecs_t *ecs, ecs_id_t sys_id, ecs_dt_t dt)
Update an individual system.
ecs_t * ecs_new(size_t entity_count, void *mem_ctx)
Creates an ECS instance.
bool ecs_has(ecs_t *ecs, ecs_id_t entity_id, ecs_id_t comp_id)
Test if entity has the specified component.
void * ecs_get(ecs_t *ecs, ecs_id_t entity_id, ecs_id_t comp_id)
Gets a component instance associated with an entity.