├── .gitattributes ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples └── example.cpp ├── img ├── component_memory_pool.png └── componentmask_version_vector.png ├── include ├── BaseEntity.h ├── BaseEntity.inl ├── ComponentManager.h ├── ComponentManager.inl ├── Defines.h ├── Entity.h ├── Entity.inl ├── EntityAlias.h ├── EntityAlias.inl ├── EntityManager.h ├── EntityManager.inl ├── Id.h ├── Id.inl ├── Iterator.h ├── Iterator.inl ├── Pool.h ├── Pool.inl ├── Property.h ├── Property.inl ├── System.h ├── System.inl ├── SystemManager.h ├── SystemManager.inl ├── UnallocatedEntity.h ├── UnallocatedEntity.inl ├── Utils.h ├── View.h ├── View.inl └── ecs.h ├── scripts ├── generate_header.py └── travis.sh ├── single_include └── ecs.h └── test ├── common ├── main.cpp └── thirdparty │ └── catch.hpp ├── ecs.cpp └── ecs_performance.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | img/componentmask_version_vector.png filter=lfs diff=lfs merge=lfs -text 2 | img/component_memory_pool.png filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System files 2 | .DS_Store 3 | 4 | # IDE files 5 | .idea/* 6 | .ycm_extra_conf.py 7 | .ycm_extra_conf.pyc 8 | 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | build*/ 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Compiled Dynamic libraries 22 | *.so 23 | *.dylib 24 | *.dll 25 | 26 | # Fortran module files 27 | *.mod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - clang 5 | - gcc 6 | 7 | sudo: false 8 | 9 | install: 10 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 11 | 12 | addons: 13 | apt: 14 | sources: 15 | - ubuntu-toolchain-r-test 16 | packages: 17 | - cmake 18 | - gcc-4.8 19 | - g++-4.8 20 | - clang 21 | 22 | 23 | script: ./scripts/travis.sh -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set( PROJ_NAME "OpenEcs" ) 4 | set( PROJ_PATH ${CMAKE_SOURCE_DIR} ) 5 | set( PROJ_OUT_PATH ${CMAKE_BINARY_DIR} ) 6 | set( PROJ_SOURCES "") 7 | set( PROJ_HEADERS "") 8 | set( PROJ_TEST_SOURCES "") 9 | set( PROJ_TEST_HEADERS "") 10 | 11 | set( CMAKE_MODULE_PATH "${PROJ_PATH}/cmake") 12 | set( EXECUTABLE_OUTPUT_PATH ${PROJ_OUT_PATH}) 13 | 14 | set( PROJ_LIB_PATH "${PROJ_PATH}/lib" ) 15 | set( PROJ_INCLUDES "${PROJ_PATH}/include") 16 | set( PROJ_TEST_INCLUDES "${PROJ_PATH}/test") 17 | set( LIB_INCLUDES "${PROJ_LIB_PATH}/include") 18 | 19 | project(${PROJ_NAME}) 20 | include(ExternalProject) 21 | 22 | # Default compiler args 23 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|.*Clang)") 24 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Werror -Wall -Wextra -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=sign-compare -std=c++11") 25 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") 26 | set(CMAKE_CXX_FLAGS_MINSIZEREL "-g -Os -DNDEBUG") 27 | set(CMAKE_CXX_FLAGS_RELEASE "-g -O2 -DNDEBUG") 28 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") 29 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 30 | # /Zi - Produces a program database (PDB) that contains type information and symbolic debugging information for use with the debugger. 31 | # /FS - Allows multiple cl.exe processes to write to the same .pdb file 32 | # /DEBUG - Enable debug during linking 33 | # /Od - Disables optimization 34 | set(CMAKE_CXX_FLAGS_DEBUG "/Zi /FS /DEBUG /Od /MDd") 35 | # /Ox - Full optimization 36 | set(CMAKE_CXX_FLAGS_RELEASE "/Ox -DNDEBUG") 37 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ox /Zi /FS /DEBUG") 38 | endif() 39 | 40 | if (NOT CMAKE_BUILD_TYPE) 41 | message("-- Defaulting to release build (use -DCMAKE_BUILD_TYPE:STRING=Debug for debug build)") 42 | set(CMAKE_BUILD_TYPE "Release") 43 | endif() 44 | 45 | # Adds sources and headers to project 46 | file( GLOB_RECURSE PROJ_SOURCES include/*.cpp) 47 | file( GLOB_RECURSE PROJ_HEADERS include/*.h include/*.inl) 48 | file( GLOB_RECURSE PROJ_TEST_SOURCES test/common/*.cpp ) 49 | file( GLOB_RECURSE PROJ_TEST_HEADERS test/common/*.hpp ) 50 | include_directories( ${PROJ_INCLUDES} ${LIB_INCLUDES} ${PROJ_TEST_INCLUDES}) 51 | 52 | # Tests 53 | enable_testing() 54 | add_executable( UnitTests ${PROJ_TEST_SOURCES} test/ecs.cpp ${PROJ_TEST_HEADERS} ${PROJ_HEADERS}) 55 | add_executable( PerformanceTests ${PROJ_TEST_SOURCES} test/ecs_performance.cpp ${PROJ_TEST_HEADERS} ${PROJ_HEADERS}) 56 | add_executable( Example examples/example.cpp ${PROJ_HEADERS}) 57 | 58 | add_test( UnitTests UnitTests) 59 | add_test( PerformanceTests PerformanceTests) 60 | 61 | install(TARGETS UnitTests DESTINATION ${PROJ_OUT_PATH}) 62 | install(TARGETS PerformanceTests DESTINATION ${PROJ_OUT_PATH}) 63 | install(TARGETS Example DESTINATION ${PROJ_OUT_PATH}) 64 | 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | A fast, clean, typesafe, C++11, header only, Entity Component System 635 | Copyright (C) 2015 Robin Grönberg 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | OpenEcs Copyright (C) 2015 Robin Grönberg 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenEcs - A fast, clean, typesafe, C++11, header only, Entity Component System 2 | 3 | [![Build Status](https://travis-ci.org/Gronis/OpenEcs.svg?branch=master)](https://travis-ci.org/Gronis/OpenEcs) [![Build status](https://ci.appveyor.com/api/projects/status/1boh2lr4gn03m939?svg=true)](https://ci.appveyor.com/project/Gronis/openecs) [![Join the chat at https://gitter.im/Gronis/OpenEcs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Gronis/OpenEcs) 4 | 5 | ## What is OpenEcs? 6 | Open Ecs is an Entity Component System that uses metaprogramming, cache coherency, and other useful tricks to maximize 7 | performance and configurability. It is written in c++11 without further dependencies. 8 | 9 | NOTE: OpenEcs is still in beta and usage with the library might change. If you need a complete ECS library for a serious 10 | project, I suggest looking further. I want more stuff like custom component allocators and perhaps a compile time 11 | configurable EntityManager and SystemManager. Some way to handle events might be useful to include. 12 | Though, I don't want to bloat the lib with too much features. Let me know what you think and what is missing, I hope you enjoy using OpenEcs. 13 | 14 | ## Why OpenEcs? 15 | I authored OpenEcs after using other ECS libraries. The main reason for this was that I wanted to write my own, and it 16 | wasn't supposed to become its own library. As I progressed, I thought it might be worth releasing it to the public as it 17 | slightly became a library with a different approach than the others. 18 | 19 | OpenECS focuses on clean and understandable code that compiles to something with good performance, with good defaults, but still providing the ability to configure alternative behaviours when necessary. 20 | 21 | ## Installation 22 | Just [Download](https://github.com/Gronis/OpenEcs/raw/master/single_include/ecs.h) the header and include it into your project. 23 | Make sure to enable c++11 when compiling. (-std=c++11 or -std=c++0x) 24 | 25 | ## Support 26 | OpenEcs support all mainstream compilers: 27 | * gcc (tested with v4.8) 28 | * clang 29 | * MSVC (Visual Studio 2015) 30 | 31 | ## Standard Features: 32 | The first thing you need is somewhere to store the entities. This is called the EntityManager and is created like this: 33 | ```cpp 34 | using namespace ecs; 35 | EntityManager entities; 36 | ``` 37 | 38 | Once the EntityManager is created. Adding entitites can be done like this 39 | 40 | ```cpp 41 | EntityManager entities; 42 | 43 | //Create one entity; 44 | Entity entity = entities.create(); 45 | 46 | //Create 100 entities 47 | vector new_entities = entities.create(100); 48 | 49 | //Create 100 entities and do something with them 50 | entities.create(100, [] (Entity entity){ 51 | //Do something with the added entity 52 | }); 53 | ``` 54 | 55 | ### Adding Components to entities 56 | Adding components to entities is easy. But first we must define a Component. A Component can be any class or struct. 57 | 58 | ```cpp 59 | 60 | struct Health{ 61 | Health(int value) : value(value) 62 | int value; 63 | }; 64 | 65 | EntityManager entities; 66 | 67 | //Create one entity; 68 | Entity entity = entities.create(); 69 | 70 | //Add health component with a value of 10 to the entity 71 | entity.add(10); 72 | 73 | //The set function can be used even if health was added before. 74 | entity.set(20); 75 | 76 | //ERROR! Add only works when it doesn't already exists. 77 | entity.add(10);// <- Assert failure, component already added 78 | ``` 79 | 80 | NOTE: In order to call the "set" method, the copy constructor for the component should not be deleted, as the copy constructor will be used if the entity already has a component of that type. However, "add" does not have this requirement, since we know that the entity does not have that component beforehand, and components without the copy constructor can be added. 81 | 82 | ```cpp 83 | struct Health{ 84 | Health(const & Health) = delete; //<-- removed copy constructor 85 | }; 86 | 87 | Entity entity = entities.create(); 88 | entity.set(10); //<-- compiler error, copy constructor deleted 89 | 90 | entity.add(10); //<-- OK 91 | ``` 92 | 93 | 94 | To reduce boilerplate code, the constructor is optional. If setting variables is then only thing that happens in the constructor, leave it be. 95 | 96 | ```cpp 97 | struct Health{ 98 | int value; 99 | }; 100 | 101 | Entity entity = entities.create(); 102 | entity.add(10); 103 | 104 | ``` 105 | NOTE: This is provided by using uniform initialization, which means that the arguments must be exactly the same as the member variables of the component 106 | 107 | ```cpp 108 | entity.add(10.0f);// <- does not work, since float is not int 109 | ``` 110 | 111 | There is also a way to add components at the same time when creating an Entity by using the "create_with" method: 112 | 113 | ```cpp 114 | entities.create_with(10, 5); //Adds health = 10 and mana = 5 115 | //OR 116 | entities.create_with(Health(10), Mana(5)); //Same as above. 117 | //This is the same as doing this: 118 | Entity entity = entity.create(); 119 | entity.add(10); 120 | entity.add(5); 121 | ``` 122 | 123 | Which one to use is up to you, though adding the components with one line gives information to the EntityManager 124 | what components are comming, and the location of the entity in memory can be probably be better. 125 | 126 | ### Accessing Components from entities 127 | Accessing components is just as easy as adding them 128 | 129 | ```cpp 130 | //Set health component with a value of 10 to the entity 131 | entity.set(10); 132 | 133 | //Access the Health Component 134 | Health& health = entity.get(); 135 | health.value = 3; 136 | 137 | //NOTE, you should use reference, not value. 138 | //Otherwise, the components will be copied and 139 | //any change made to it will not be registered 140 | //on the actual component 141 | 142 | //NOTE: Do not use (unless you know what you are doing) 143 | Health health = entity.get(); 144 | health.value = 3;// <- does not change health of entity, 145 | // because variable is copied 146 | 147 | ``` 148 | Accessing a component which does not exist on a specified entity will trigger a runtime assertion. To check if an entity has a specified component, use the "has" function. 149 | 150 | ```cpp 151 | Entity entity = entities.create(); 152 | bool has_health = entity.has(); //Returns false 153 | 154 | entity.add(10); 155 | has_health = entity.has(); //Returns true 156 | 157 | ``` 158 | 159 | ### Removing Entities and Components 160 | 161 | Destroying an entity can be done using the destroy method 162 | ```cpp 163 | Entity entity = entities.create(); 164 | entity.destroy(); 165 | //or 166 | entities.destroy(entity); 167 | ``` 168 | 169 | Removing Components is done by using the remove function 170 | ```cpp 171 | Entity entity = entities.create(); 172 | entity.add(0); 173 | entity.remove(); 174 | ``` 175 | Destroying an entity removes all components and calls their destructors. it also opens that memory slot for another entity to take its place. 176 | 177 | When an entity is destroyed. It is no longer valid, and any action used with it should not work 178 | ```cpp 179 | entity.destroy(); 180 | //Does not work 181 | entity.add(0); // <- triggers runtime assertion, Entity invalid 182 | 183 | //Check if entity is valid 184 | bool is_valid = entity.is_valid(); 185 | ``` 186 | 187 | To track if an entity is valid. OpenEcs associates each entity with a version when accessed from the EntityManager. Whenever an entity is destroyed, the version for that Entity changes, and all entities with the old versions are invalid, as they no longer exists. 188 | 189 | ### Iterating through the EntityManager 190 | To access entities with certain components. There is a "with" function that looks like this 191 | 192 | ```cpp 193 | EntityManager entities; 194 | 195 | //Iterate through the entities, grabbing each 196 | //entity with Health and Mana component 197 | for(Entity entity : entities.with()){ 198 | entity.get(); //Do things with health 199 | entity.get(); //Do things with mana 200 | } 201 | 202 | //There is also a functional style iteration that works 203 | //with a provided lambda. 204 | entities.with([](Health& health, Mana& mana){ 205 | health.value = 2; //Do stuff 206 | }); 207 | 208 | //If you need the entity, include it as well 209 | entities.with([](Health& health, Mana& mana, Entity entity){ 210 | health.value = 2; //Do stuff 211 | entity.remove(); 212 | }); 213 | 214 | ``` 215 | 216 | NOTE, use reference, not values as parameters. (with the exception of Entity). Otherwise, the components will be copied and any change made to it will not be registered on the actual component. 217 | 218 | 219 | ```cpp 220 | //Don't forget to use references ---- 221 | // | \ 222 | // v v 223 | entities.with([](Health & health, Mana & mana){ }); 224 | 225 | ``` 226 | 227 | ### Systems 228 | Systems define our behavior. The SystemManager provided by OpenEcs is very simple and is just a wrapper around an interface with an update function, together with the entities. 229 | 230 | First we define our SystemManager 231 | 232 | ```cpp 233 | EntityManager entities; 234 | //We must provide what entities we like to work with 235 | SystemManager systems(entities); 236 | ``` 237 | 238 | Then we create a system class. 239 | Any new system class must inherit the System class like this: 240 | 241 | ```cpp 242 | class RemoveCorpsesSystem : public System{ 243 | public: 244 | void update(float time) override { 245 | // Get the entity manager using entities() function 246 | for(auto entity : entities().with()){ 247 | if(entity.get().value <= 0){ 248 | //Destroy the entity 249 | entity.destroy(); 250 | } 251 | } 252 | } 253 | }; 254 | 255 | EntityManager entities; 256 | SystemManager systems(entities); 257 | 258 | systems.add( /*Provide any constructor arguments*/ ); 259 | 260 | //Here we update all systems 261 | float deltaTime = 1; 262 | systems.update(deltaTime); //Updates RemoveCorpsesSystem 263 | 264 | //We can also remove systems when we do not need them 265 | systems.remove(); 266 | systems.update(deltaTime); //Does not updates any system 267 | 268 | ``` 269 | 270 | The systems are updated in the same order as they are added. 271 | 272 | ### Error handling 273 | Any runtime or compile-time error should be handled by static or runtime assertions. 274 | 275 | ## Extra features 276 | Aside from the normal usage of bitmasks for Component recognition and storing components in a vector for cache 277 | coherency, OpenEcs has some extra features. 278 | 279 | ### EntityAlias that enables object oriented interaction. 280 | 281 | As an object oriented programmer, the ability to encapsulate the implementation as much as possible is desired. By using 282 | EntityAlias, an Entity can be wrapped inside these, witch then define all functions needed. then, the systems can 283 | interact with these EntityAliases instead of the entity. This takes no extra performance, and creates an abstraction 284 | later between actual ECS implementation and the desired action. 285 | 286 | Defining an EntityAlias 287 | ```cpp 288 | class Actor : public EntityAlias{ 289 | public: 290 | //here we can hide the implementation details of how the components 291 | //are used by the entity, and provide ordinary functions instead. 292 | void kill(){ 293 | get().value = 0; 294 | } 295 | 296 | bool attack(Entity target){ 297 | if(!has()){ 298 | add(target); 299 | return true; 300 | } 301 | return false; 302 | } 303 | }; 304 | ``` 305 | 306 | NOTE: An EntityAlias class cannot define new member variables. It is only a wrapper between the 307 | entity and the actual components. If new data needs to be associated with the entity, introduce 308 | new components instead 309 | 310 | The EntityAlias assumes that the entity has all components provided by the inheritance. 311 | We can get every entity that can be associated as an Actor (all entities with Health and Name in this case) by using the 312 | "fetch_every" function, or a from a single Entity using the "as" function. 313 | 314 | ```cpp 315 | EntityManager entities; 316 | 317 | Entity entity = entities.create(); 318 | entity.add(0); 319 | entity.add(""); 320 | 321 | // Access all entities that has all required components that an Actor has 322 | for(Actor actor : entities.fetch_every()){ 323 | actor.kill(); //For example 324 | } 325 | //or with lambda 326 | entities.fetch_every([](Actor& actor){ 327 | actor.kill(); 328 | }); 329 | 330 | //Access one entity as EntityAlias 331 | Actor actor = entity.as(); 332 | ``` 333 | 334 | The sweetness with using the "fetch_every" function is that it basically generates the same code as this: 335 | 336 | ```cpp 337 | EntityManager entities; 338 | 339 | for(auto entity : entities.with()){ 340 | entity.get().value = 0; // <- same as actor.kill(); 341 | } 342 | ``` 343 | 344 | This is very useful, as we get another abstraction layer without giving up performance. 345 | 346 | 347 | NOTE: the "as" function will result in a runtime assertion error if the 348 | entity does not have the required components (Health and Name in this case) 349 | 350 | Use "is" function to test if entity can be observed as an Actor 351 | 352 | ```cpp 353 | // The "is" function will test if entity has all required components for Actor 354 | if(entity.is()){ // <- same as entity.has() 355 | Actor actor = entity.as(); // <- Safe to Access Entity Alias 356 | } 357 | ``` 358 | 359 | 360 | ### Creating entities by using EntityAlias 361 | By defining a constructor for the EntityAlias. We can use the create function from the EntityManager 362 | like this: 363 | 364 | ```cpp 365 | class Actor : public EntityAlias{ 366 | // ^ ^ 367 | // \_____| 368 | public:// | 369 | Actor(int health, std::string name){// | 370 | add(health);// | 371 | add(name);// | 372 | //Make sure to add all required components. 373 | //If any required component is not added, 374 | //this will cause a runtime assertion failure 375 | 376 | //it is also important to use "add" instead of "set" 377 | //as "set" assumes that all required components are 378 | //already set for this entity (Since they are 379 | //required). 380 | } 381 | }; 382 | ``` 383 | 384 | Once we have the EntityAlias constructor, we can create 385 | an entity using the "create" function. 386 | 387 | ```cpp 388 | EntityManager entities; 389 | 390 | //Create an actor with 10 health and named Evil Dude 391 | Actor actor = entities.create(10, "Evil Dude"); 392 | 393 | ``` 394 | This results in a useful factory method for any EntityAlias. Another good thing is that what an Entity is, is defined by 395 | its components, and enables entities to be several things at the same time, without using hierarchy (Which is the idea 396 | behind Entity Component Systems). 397 | 398 | ### Wait a bit... 399 | 400 | You might start thinking "If all components should be added anyway. Why is the constructor needed?" I started thinking that too, and added a standard behavior. 401 | 402 | Considering the following code: 403 | 404 | ```cpp 405 | //Components 406 | struct Name { 407 | str::string value; 408 | }; 409 | struct Height { 410 | int value; 411 | }; 412 | struct Weight { 413 | int value; 414 | }; 415 | 416 | //Since components have optional constructors, We can type like this 417 | //to create an Actor (Entity with Name, Height and Weight) 418 | auto entity = entities.create(); 419 | entity.add("Darth Vader"); 420 | entity.add(180); 421 | entity.add(75); 422 | 423 | //But we want to use the EntityAlias class, because we know what an Actor is. 424 | //However, we don't want to write the constructor, because enough is specified 425 | //for the library to "figure out" how to create an Actor after specifying what 426 | //components it has. Therefore, we leave out the constructor. 427 | class Actor : public EntityAlias {}; 428 | 429 | //And then create an Actor like this 430 | entities.create("Darth Vader", 180, 75); 431 | // ^ ^ ^ 432 | // | | | 433 | // Assumes constructor for Name / | 434 | // Assumes constructor for Height ---´ / 435 | // Assumes constructor for Weight -------´ 436 | 437 | //create assumes that each parameter can add 438 | //required components with that single argument. 439 | class Actor : public EntityAlias { 440 | //No constructor means the same thing as this constructor 441 | Actor(std::string name, int height, int weight){ 442 | add(name); 443 | add(height); 444 | add(weight); 445 | } 446 | }; 447 | ``` 448 | 449 | NOTE: This does not work when a required components needs more than one argument when creating the object. This is true with components like 2D velocity, and 2D position: 450 | 451 | ```cpp 452 | //Components with MORE than 1 member variable 453 | struct Velocity{ int x,y; }; 454 | struct Position{ int x,y; }; 455 | 456 | //EntityAlias 457 | struct Movable : EntityAlias{}; 458 | 459 | entities.create(/*What to write here?*/); 460 | 461 | //In order to create a movable without providing a constructor 462 | //We must write like this. 463 | entities.create(Position{0,0}, Velocity{0,0}); 464 | //This calls the standard copy constructor for Position and 465 | //Velocity component. 466 | 467 | //If we want to be able to write something like this: 468 | entities.create(0,0,0,0); 469 | //We need to provide the following constructor, since 470 | //there is not enough information specified for the 471 | //library to understand how to add create a Movable. 472 | struct Movable : EntityAlias{ 473 | Movable(int posX, int posY, int velX, int velY){ 474 | add(posX, posY); 475 | add(velX, velY); 476 | } 477 | }; 478 | ``` 479 | 480 | Hopefully, this should help with quick creation of components and EntityAliases without typing "boilerplate code" for the lazy people (like me), while still allowing the flexibility of defining your own constructors when necessary. 481 | 482 | 483 | ### Override operators 484 | It would be useful if the components override some basic operators for cleaner code. 485 | ```cpp 486 | //Using == operator of component class 487 | bool health_equals_mana = 488 | entity.get() == entity.get(); 489 | 490 | //If no operators are defined, we must write it like this 491 | bool health_equals_mana = 492 | entity.get().value == entity.get().value; 493 | ``` 494 | However, defining operators for each component is just useless boilerplate code can be very anoying, time consuming, and difficult to write. If the component only has one property, in the case of Health in this example. There is a class called Property\ which overrides useful operators. The Component class may extend this class to inherit some basic operators. 495 | 496 | ```cpp 497 | struct Health : Property{ 498 | //Define constructor like this 499 | Health(int value) : Property(value){}; 500 | //Or like this 501 | Health(int value){ 502 | this->value = value; 503 | }; 504 | }; 505 | 506 | // Or skip the constructor entirely, like with usual components. 507 | // However the assignment operator will not work without 508 | // a given constructor. 509 | struct Health : Property{}; 510 | 511 | //Needs a constructor for assignment like this to work 512 | entity.get() = 4;// <- needs a constructor to enable assignment 513 | 514 | ``` 515 | 516 | Now we should have some useful operator implementations for the health component which enables cleaner code 517 | 518 | ```cpp 519 | // operator > 520 | if(entity.get() > 10){ 521 | 522 | } 523 | // without using operator > 524 | if(entity.get().value > 10){ 525 | 526 | } 527 | ``` 528 | NOTE: Remember that any class can be a component, and that it is optional to use Property\, as overriding operators is not always desired. For those who does not what to use overridden operators, leave out the Property\ class. 529 | 530 | ### Memory 531 | 532 | The memory for components and entities are handled manually by OpenEcs. Entities are nothing more than a placeholder with its ID, and a pointer to its EntityManager. Entities are not stored, but created when returned from the API. 533 | 534 | The ID for an entity is its index (Where its located) and a version (When reusing old indexes for removed entities). All versions for an Entity is stored in a std::vector continuously in memory, as a number. 535 | 536 | Every component type gets its own id and a component mask, which tracks what components each entity has. These masks are placed in a std::vector, like the versions. Each bit represents if an entity has a component. By default, the component mask size is 64bits, which means that the max number of component types that can be used with OpenEcs is 64. This can be changed with a define like this BEFORE including the header. In this example, each component mask will be 128bits, and can therefore, OpenEcs will be able to keep track of 128 different component types. 537 | 538 | ```cpp 539 | #define ECS_MAX_NUM_OF_COMPONENTS 128 540 | #include "ecs/ecs.h" 541 | ``` 542 | 543 | 544 | 545 | Each component type is handled by a component manager, which is basically a memory pool. The chunk size for a component manager's memory pool is 64 * s bytes, where s is the size of the component type for the component manager. The number 64 is there because one chunk should at least fit on one or more cachelines in memory. There is no point in making it larger, and smaller reduces performance. Just as the versions, each component is located at the index for its entity ID. 546 | 547 | 548 | 549 | The EntityManager allocates memory for each entity to have every component. This might sound stupid, but once memory is allocated, not using it does not cost any cpu time, and we still want the opportunity to add any component to an entity. However it's not cheap to load memory into the cpu. Therefore, the EntityManager tries to put "similar" entities together in memory when they are created. More about this can be read in the Performance section. 550 | 551 | 552 | 553 | ### Performance 554 | 555 | On my Apple MBP Retina 13" 2014 computer, this is some performance tests using 10 000 000 entities with some components 556 | attached: 557 | 558 | | Operation | Time | 559 | |-------------------------------------------------------|---------------| 560 | | create() | 0.38s | 561 | | create(10M) | 0.17257s | 562 | | destroy() | 0.16733s | 563 | | normal for-loop with if-case | 0.00652s | 564 | | iterating using with for-loop (without unpacking) | 0.00847s | 565 | | iterating using with for-loop (unpack one component) | 0.01125s | 566 | | iterating using with lambda (unpack one component) | 0.01125s | 567 | | iterating using with for-loop (unpack two components) | 0.01600s | 568 | | iterating using with lambda (unpack two components) | 0.01600s | 569 | | iterating using fetch_every for-loop | 0.00812s | 570 | | iterating using fetch_every lambda | 0.00812s | 571 | 572 | To improve performance, there are some guidelines: 573 | 574 | Create entities by using EntityAlias or with "create_with\" function, as this gives the EntityManager an idea were to put the entities. The EntityManager tries to clump similar entities together in memory, in order to reduce cache misses when loading memory into the cache CPU memory. When creating entities like this: 575 | 576 | ```cpp 577 | // The EntityManager has no idea what components are coming 578 | // and and will probably put this entity near other entities 579 | // that don't have similar components. 580 | Entity e = entities.create(); 581 | e.add(); 582 | e.add(); 583 | ``` 584 | 585 | By doing this instead: 586 | 587 | ```cpp 588 | // Here the EntityManager knows what components the Entity will 589 | // have, and will put it close to other entities which also had 590 | // these components when created. 591 | Entity e = entities.create_with(/* Args here */); 592 | //or 593 | auto e = entities.create(/* Constructor args for SomeEntityAlias here */); 594 | ``` 595 | 596 | NOTE: Of course this means that adding or removing components from entities will result in cache misses anyway. This cannot be resolved, unless the entity components is moved in memory, which breaks the idea of that the index is part of the Entity ID. However, it is most likely that entities will retain most of its components over its lifecycle, and therefore, providing this information should result in a performance boost. 597 | 598 | 599 | To improve performance iterate by using auto when iterating with a for loop 600 | 601 | ```cpp 602 | for(auto entity : entities.with()){ 603 | // ^ 604 | // \ 605 | // ` The type here will not become Entity, it will become EntityAlias. 606 | // It will only use the normal Entity class if you tell it to. 607 | } 608 | 609 | ``` 610 | When you use the function "get\" on the Entity object,we need to perform some runtime checks. If you are using EntityAlias\, the compiler assumes that the entity has Health and Name components, and bypasses this check when accessing the component. We already do this runtime check when we iterate through the list. Why do it again? 611 | 612 | ```cpp 613 | for(auto entity : entities.with()){ 614 | entity.get(); // <- No runtime check. Fast 615 | entity.get(); // <- Runtime check required. 616 | } 617 | 618 | ``` 619 | 620 | Or just use the lambda version of "with" or "fetch_every" function with your own EntityAliases, and you 621 | are good to go! 622 | 623 | ```cpp 624 | entities.with([](Health& health, Name& name){ }); 625 | //or 626 | entities.fetch_every([](Actor actor){ }); 627 | 628 | ``` 629 | 630 | ## A complete example 631 | This examples illustrates two spellcasters: Alice and Bob, and their fight to the death. Prints the winner and remaining 632 | health and mana. 633 | 634 | ```cpp 635 | #include 636 | #include "ecs/ecs.h" 637 | 638 | using namespace ecs; 639 | 640 | //Components as properties, we skip defining a constructor, since we 641 | //don't do anything unusual. 642 | struct Health : Property{}; 643 | struct Mana : Property{}; 644 | struct Name : Property{}; 645 | 646 | struct Spellcaster : public EntityAlias{ 647 | //We don't need to define a constructor since all of 648 | //our components only have 1 member. 649 | bool isOom(){ 650 | return get() == 0;// <- override == operator 651 | } 652 | 653 | bool isAlive(){ 654 | return get() > 0; 655 | } 656 | void castSpell(Spellcaster& target){ 657 | if(!isOom()){ 658 | --get(); 659 | --target.get(); 660 | } 661 | } 662 | }; 663 | 664 | class RemoveCorpsesSystem : public System{ 665 | public: 666 | void update(float time) override { 667 | //Method 1 668 | //Any health entity 669 | entities().with([](Health& health, Entity entity){ 670 | if(health <= 0){ 671 | entity.destroy(); 672 | } 673 | }); 674 | //Method 2 675 | //Any spellcaster that is dead 676 | entities().fetch_every([](Spellcaster& spellcaster){ 677 | if(!spellcaster.isAlive()){ 678 | spellcaster.destroy(); 679 | } 680 | }); 681 | } 682 | }; 683 | 684 | class CastSpellSystem : public System{ 685 | public: 686 | void update(float time) override { 687 | entities().fetch_every([&] (Spellcaster& spellcaster1){ 688 | entities().fetch_every([&] (Spellcaster& spellcaster2){ 689 | if(spellcaster1 != spellcaster2){ 690 | spellcaster1.castSpell(spellcaster2); 691 | } 692 | }); 693 | }); 694 | } 695 | }; 696 | 697 | class GiveManaSystem : public System{ 698 | public: 699 | void update(float time) override { 700 | entities().fetch_every([] (Spellcaster& spellcaster){ 701 | if(spellcaster.isOom()) spellcaster.set(1337); 702 | }); 703 | } 704 | }; 705 | 706 | class Game{ 707 | public: 708 | Game() : systems(entities) {} 709 | void run(){ 710 | systems.add(); 711 | systems.add(); 712 | systems.add(); 713 | entities.create("Alice", 8, 12); 714 | entities.create("Bob", 12, 8); 715 | while(entities.count() > 1) systems.update(1); 716 | entities.with([] (Name& name, Health& health, Mana& mana){ 717 | std::cout << name << " won!" << std::endl; 718 | std::cout << "Health: " << health << std::endl; 719 | std::cout << "Mana: " << mana << std::endl; 720 | }); 721 | } 722 | 723 | private: 724 | EntityManager entities; 725 | SystemManager systems; 726 | }; 727 | 728 | int main(){ 729 | Game game; 730 | game.run(); 731 | } 732 | ``` 733 | 734 | Output: 735 | ``` 736 | Bob won! 737 | Health: 4 738 | Mana: 1337 739 | ``` 740 | 741 | ## License 742 | Copyright (C) 2015 Robin Grönberg 743 | 744 | This program is free software: you can redistribute it and/or modify 745 | it under the terms of the GNU General Public License as published by 746 | the Free Software Foundation, either version 3 of the License, or 747 | (at your option) any later version. 748 | 749 | This program is distributed in the hope that it will be useful, 750 | but WITHOUT ANY WARRANTY; without even the implied warranty of 751 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 752 | GNU General Public License for more details. 753 | 754 | You should have received a copy of the GNU General Public License 755 | along with this program. If not, see . 756 | -------------------------------------------------------------------------------- /examples/example.cpp: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------- 2 | /// Copyright (C) 2015 Robin Grönberg 3 | /// 4 | /// This program is free software: you can redistribute it and/or modify 5 | /// it under the terms of the GNU General Public License as published by 6 | /// the Free Software Foundation, either version 3 of the License, or 7 | /// (at your option) any later version. 8 | /// 9 | /// This program is distributed in the hope that it will be useful, 10 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | /// GNU General Public License for more details. 13 | /// 14 | /// You should have received a copy of the GNU General Public License 15 | /// along with this program. If not, see . 16 | 17 | #include 18 | #include "ecs.h" 19 | #include "ecs.h" 20 | 21 | using namespace ecs; 22 | 23 | //Components as properties, we skip defining a constructor, since we 24 | //don't do anything unusual. 25 | 26 | struct Health : Property{}; 27 | struct Mana : Property{}; 28 | struct Name : Property{}; 29 | 30 | struct Spellcaster : public EntityAlias{ 31 | //We don't need to define a constructor since all of 32 | //our components only have 1 member. 33 | bool isOom(){ 34 | return get() == 0;// <- override == operator 35 | } 36 | 37 | bool isAlive(){ 38 | return get() > 0; 39 | } 40 | void castSpell(Spellcaster& target){ 41 | if(!isOom()){ 42 | --get(); 43 | --target.get(); 44 | } 45 | } 46 | }; 47 | 48 | class RemoveCorpsesSystem : public System{ 49 | public: 50 | void update(float time) override { 51 | //Method 1 52 | //Any health entity 53 | entities().with([](Health& health, Entity entity){ 54 | if(health <= 0){ 55 | entity.destroy(); 56 | } 57 | }); 58 | //Method 2 59 | //Any spellcaster that is dead 60 | entities().fetch_every([](Spellcaster& spellcaster){ 61 | if(!spellcaster.isAlive()){ 62 | spellcaster.destroy(); 63 | } 64 | }); 65 | } 66 | }; 67 | 68 | class CastSpellSystem : public System{ 69 | public: 70 | void update(float time) override { 71 | entities().fetch_every([&] (Spellcaster& spellcaster1){ 72 | entities().fetch_every([&] (Spellcaster& spellcaster2){ 73 | if(spellcaster1 != spellcaster2){ 74 | spellcaster1.castSpell(spellcaster2); 75 | } 76 | }); 77 | }); 78 | } 79 | }; 80 | 81 | class GiveManaSystem : public System{ 82 | public: 83 | void update(float time) override { 84 | entities().fetch_every([] (Spellcaster& spellcaster){ 85 | if(spellcaster.isOom()) spellcaster.set(1337); 86 | }); 87 | } 88 | }; 89 | 90 | class Game{ 91 | public: 92 | Game() : systems(entities) {} 93 | void run(){ 94 | systems.add(); 95 | systems.add(); 96 | systems.add(); 97 | entities.create("Alice", 8, 12); 98 | entities.create("Bob", 12, 8); 99 | while(entities.count() > 1) systems.update(1); 100 | entities.with([] (Name& name, Health& health, Mana& mana){ 101 | std::cout << name << " won!" << std::endl; 102 | std::cout << "Health: " << health << std::endl; 103 | std::cout << "Mana: " << mana << std::endl; 104 | }); 105 | } 106 | 107 | private: 108 | EntityManager entities; 109 | SystemManager systems; 110 | }; 111 | 112 | int main(){ 113 | Game game; 114 | game.run(); 115 | } -------------------------------------------------------------------------------- /img/component_memory_pool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gronis/OpenEcs/2a569b92a9abb9a03d02144f70ff5055fa306efd/img/component_memory_pool.png -------------------------------------------------------------------------------- /img/componentmask_version_vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gronis/OpenEcs/2a569b92a9abb9a03d02144f70ff5055fa306efd/img/componentmask_version_vector.png -------------------------------------------------------------------------------- /include/BaseEntity.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Robin Grönberg on 05/12/15. 3 | // 4 | 5 | #ifndef OPENECS_BASEENTITY_H 6 | #define OPENECS_BASEENTITY_H 7 | 8 | #include "Entity.h" 9 | #include "EntityManager.h" 10 | 11 | 12 | namespace ecs { 13 | 14 | namespace details { 15 | 16 | class BaseEntity { 17 | public: 18 | inline BaseEntity(const Entity &entity); 19 | protected: 20 | inline BaseEntity(); 21 | inline BaseEntity(EntityManager* manager); 22 | inline BaseEntity(const BaseEntity &other); 23 | inline BaseEntity& operator=(const BaseEntity& other) { entity_ = other.entity_; return *this; } 24 | 25 | inline EntityManager &entities() { return *manager_; } 26 | inline Entity &entity() { return entity_; } 27 | inline EntityManager const& entities() const { return *manager_; } 28 | inline Entity const& entity() const { return entity_; } 29 | private: 30 | union { 31 | EntityManager *manager_; 32 | Entity entity_; 33 | }; 34 | }; //BaseEntity 35 | 36 | } // namespace details 37 | 38 | } // namespace ecs 39 | 40 | #include "BaseEntity.inl" 41 | 42 | #endif //OPENECS_BASEENTITY_H 43 | -------------------------------------------------------------------------------- /include/BaseEntity.inl: -------------------------------------------------------------------------------- 1 | #include "BaseEntity.h" 2 | 3 | namespace ecs { 4 | 5 | namespace details { 6 | 7 | BaseEntity::BaseEntity(const Entity &entity) : entity_(entity) { } 8 | BaseEntity::BaseEntity() { } 9 | BaseEntity::BaseEntity(EntityManager * manager) : manager_(manager){ } 10 | BaseEntity::BaseEntity(const BaseEntity &other) : entity_(other.entity_) { } 11 | 12 | } // namespace details 13 | 14 | } // namespace ecs 15 | -------------------------------------------------------------------------------- /include/ComponentManager.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_COMPONENTMANAGER_H 2 | #define ECS_COMPONENTMANAGER_H 3 | 4 | #include "Defines.h" 5 | namespace ecs{ 6 | 7 | // Forward declarations 8 | class EntityManager; 9 | 10 | namespace details{ 11 | 12 | // Forward declarations 13 | class BaseProperty; 14 | 15 | ///----------------------------------------------------------------------- 16 | /// global function for creating a component at a specific location 17 | ///----------------------------------------------------------------------- 18 | template 19 | auto create_component(void* ptr, Args && ... args) -> 20 | typename std::enable_if::value, C&>::type; 21 | 22 | // Creating a component that doesn't have ctor, and is not a property -> create using uniform initialization 23 | template 24 | auto create_component(void* ptr, Args && ... args) -> 25 | typename std::enable_if::value && 26 | !std::is_base_of::value, C&>::type; 27 | 28 | // Creating a component that doesn't have ctor, and is a property -> create using underlying Property ctor 29 | template 30 | auto create_component(void* ptr, Args && ... args) -> 31 | typename std::enable_if< 32 | !std::is_constructible::value && 33 | std::is_base_of::value, C&>::type; 34 | 35 | 36 | ///--------------------------------------------------------------------- 37 | /// Helper class, all ComponentManager are a BaseManager 38 | ///--------------------------------------------------------------------- 39 | class BaseManager { 40 | public: 41 | virtual ~BaseManager() { }; 42 | virtual void remove(index_t index) = 0; 43 | virtual ComponentMask mask() = 0; 44 | virtual void* get_void_ptr(index_t index) = 0; 45 | virtual void const* get_void_ptr(index_t index) const = 0; 46 | virtual void ensure_min_size(index_t size) = 0; 47 | }; 48 | 49 | ///--------------------------------------------------------------------- 50 | /// Helper class, This is the main class for holding many Component of 51 | /// a specified type. It uses a memory pool to store the components 52 | ///--------------------------------------------------------------------- 53 | template 54 | class ComponentManager: public BaseManager, details::forbid_copies { 55 | public: 56 | ComponentManager(EntityManager &manager, size_t chunk_size = ECS_DEFAULT_CHUNK_SIZE); 57 | 58 | /// Allocate and create at specific index, using constructor 59 | template 60 | C& create(index_t index, Args &&... args); 61 | 62 | /// Remove component at specific index and call destructor 63 | void remove(index_t index); 64 | 65 | /// Access a component given a specific index 66 | C& operator[](index_t index); 67 | C& get(index_t index); 68 | C const& get(index_t index) const; 69 | 70 | /// Access a ptr to a component given a specific index 71 | C* get_ptr(index_t index); 72 | C const* get_ptr(index_t index) const; 73 | 74 | /// Access a raw void ptr to a component given a specific index 75 | void* get_void_ptr(index_t index); 76 | void const* get_void_ptr(index_t index) const; 77 | 78 | // Ensures the pool that at it has the size of at least size 79 | void ensure_min_size(index_t size); 80 | 81 | /// Get the bitmask for the component this ComponentManger handles 82 | ComponentMask mask(); 83 | 84 | private: 85 | EntityManager &manager_; 86 | details::Pool pool_; 87 | }; //ComponentManager 88 | 89 | } // namespace details 90 | 91 | } // namespace ecs 92 | 93 | #include "ComponentManager.inl" 94 | 95 | #endif //ECS_COMPONENTMANAGER_H 96 | -------------------------------------------------------------------------------- /include/ComponentManager.inl: -------------------------------------------------------------------------------- 1 | #include "EntityManager.h" 2 | #include "ComponentManager.h" 3 | #include "Defines.h" 4 | 5 | namespace ecs{ 6 | 7 | namespace details{ 8 | 9 | template 10 | ComponentManager::ComponentManager(EntityManager &manager, size_t chunk_size) : 11 | manager_(manager), 12 | pool_(chunk_size) 13 | { } 14 | 15 | // Creating a component that has a defined ctor 16 | template 17 | auto create_component(void* ptr, Args && ... args) -> 18 | typename std::enable_if::value, C&>::type { 19 | return *new(ptr) C(std::forward(args)...); 20 | } 21 | 22 | // Creating a component that doesn't have ctor, and is not a property -> create using uniform initialization 23 | template 24 | auto create_component(void* ptr, Args && ... args) -> 25 | typename std::enable_if::value && 26 | !std::is_base_of::value, C&>::type { 27 | return *new(ptr) C{std::forward(args)...}; 28 | } 29 | 30 | // Creating a component that doesn't have ctor, and is a property -> create using underlying Property ctor 31 | template 32 | auto create_component(void* ptr, Args && ... args) -> 33 | typename std::enable_if< 34 | !std::is_constructible::value && 35 | std::is_base_of::value, C&>::type { 36 | static_assert(sizeof...(Args) <= 1, ECS_ASSERT_MSG_ONLY_ONE_ARGS_PROPERTY_CONSTRUCTOR); 37 | return *reinterpret_cast(new(ptr) typename C::ValueType(std::forward(args)...)); 38 | } 39 | 40 | template template 41 | C& ComponentManager::create(index_t index, Args &&... args) { 42 | pool_.ensure_min_size(index + 1); 43 | create_component(get_ptr(index), std::forward(args)...); 44 | return get(index); 45 | } 46 | 47 | template 48 | void ComponentManager::remove(index_t index) { 49 | pool_.destroy(index); 50 | manager_.mask(index).reset(component_index()); 51 | } 52 | 53 | template 54 | C &ComponentManager::operator[](index_t index){ 55 | return get(index); 56 | } 57 | 58 | template 59 | C &ComponentManager::get(index_t index) { 60 | return *get_ptr(index); 61 | } 62 | 63 | template 64 | C const &ComponentManager::get(index_t index) const { 65 | return *get_ptr(index); 66 | } 67 | 68 | template 69 | C *ComponentManager::get_ptr(index_t index) { 70 | return pool_.get_ptr(index); 71 | } 72 | 73 | template 74 | C const *ComponentManager::get_ptr(index_t index) const { 75 | return pool_.get_ptr(index); 76 | } 77 | 78 | template 79 | void *ComponentManager::get_void_ptr(index_t index) { 80 | return pool_.get_ptr(index); 81 | } 82 | 83 | template 84 | void const *ComponentManager::get_void_ptr(index_t index) const { 85 | return pool_.get_ptr(index); 86 | } 87 | 88 | template 89 | void ComponentManager::ensure_min_size(index_t size){ 90 | pool_.ensure_min_size(size); 91 | } 92 | 93 | template 94 | ComponentMask ComponentManager::mask() { 95 | return component_mask(); 96 | } 97 | 98 | } // namespace details 99 | 100 | } // namespace ecs -------------------------------------------------------------------------------- /include/Defines.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_DEFINES_H 2 | #define ECS_DEFINES_H 3 | 4 | #include 5 | 6 | /// The cache line size for the processor. Usually 64 bytes 7 | #ifndef ECS_CACHE_LINE_SIZE 8 | #define ECS_CACHE_LINE_SIZE 64 9 | #endif 10 | 11 | /// This is how an assertion is done. Can be defined with something else if needed. 12 | #ifndef ECS_ASSERT 13 | #define ECS_ASSERT(Expr, Msg) assert(Expr && Msg) 14 | #endif 15 | 16 | /// The maximum number of component types the EntityManager can handle 17 | #ifndef ECS_MAX_NUM_OF_COMPONENTS 18 | #define ECS_MAX_NUM_OF_COMPONENTS 64 19 | #endif 20 | 21 | /// How many components each block of memory should contain 22 | /// By default, this is divided into the same size as cache-line size 23 | #ifndef ECS_DEFAULT_CHUNK_SIZE 24 | #define ECS_DEFAULT_CHUNK_SIZE ECS_CACHE_LINE_SIZE 25 | #endif 26 | 27 | #define ECS_ASSERT_IS_CALLABLE(T) \ 28 | static_assert(details::is_callable::value, \ 29 | "Provide a function or lambda expression"); \ 30 | 31 | #define ECS_ASSERT_IS_ENTITY(T) \ 32 | static_assert(std::is_base_of::value || \ 33 | std::is_same::value , \ 34 | #T " does not inherit EntityAlias."); 35 | 36 | #define ECS_ASSERT_ENTITY_CORRECT_SIZE(T) \ 37 | static_assert(sizeof(details::BaseEntity) == sizeof(T), \ 38 | #T " should not include new variables, add them as Components instead."); \ 39 | 40 | #define ECS_ASSERT_VALID_ENTITY(E) \ 41 | ECS_ASSERT(is_valid(E), "Entity is no longer valid"); \ 42 | 43 | #define ECS_ASSERT_IS_SYSTEM(S) \ 44 | static_assert(std::is_base_of::value, \ 45 | "DirivedSystem must inherit System."); \ 46 | 47 | #define ECS_ASSERT_MSG_ONLY_ONE_ARGS_PROPERTY_CONSTRUCTOR \ 48 | "Creating a property component should only take 1 argument. " \ 49 | "If component should initilize more members, provide a " \ 50 | "constructor to initilize property component correctly" \ 51 | 52 | namespace ecs{ 53 | /// Type used for entity index 54 | using index_t = uint32_t; 55 | /// Type used for entity version 56 | using version_t = uint8_t; 57 | 58 | namespace details{ 59 | 60 | /// ComponentMask is a mask defining what components and entity has. 61 | using ComponentMask = std::bitset; 62 | 63 | } // namespace details 64 | } // namespace ecs 65 | 66 | #endif //ECS_DEFINES_H 67 | -------------------------------------------------------------------------------- /include/Entity.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_ENTITY_H 2 | #define ECS_ENTITY_H 3 | 4 | #include "Id.h" 5 | 6 | namespace ecs{ 7 | ///--------------------------------------------------------------------- 8 | /// Entity is the identifier of an identity 9 | ///--------------------------------------------------------------------- 10 | /// 11 | /// An entity consists of an id and version. The version is used to 12 | /// ensure that new entities allocated with the same id are separable. 13 | /// 14 | /// An entity becomes invalid when destroyed. 15 | /// 16 | ///--------------------------------------------------------------------- 17 | class Entity { 18 | public: 19 | inline Entity(EntityManager *manager, Id id); 20 | inline Entity &operator=(const Entity &rhs); 21 | 22 | inline Id &id() { return id_; } 23 | inline Id const &id() const { return id_; } 24 | 25 | /// Returns the requested component, or error if it doesn't exist 26 | template inline C &get(); 27 | template inline C const &get() const; 28 | 29 | /// Set the requested component, if old component exist, 30 | /// a new one is created. Otherwise, the assignment operator 31 | /// is used. 32 | template inline C &set(Args &&... args); 33 | 34 | /// Add the requested component, error if component of the same type exist already 35 | template inline C &add(Args &&... args); 36 | 37 | /// Access this Entity as an EntityAlias. 38 | template inline T &as(); 39 | template inline T const &as() const; 40 | 41 | /// Assume that this entity has provided Components 42 | /// Use for faster component access calls 43 | template inline EntityAlias &assume(); 44 | template inline EntityAlias const &assume() const; 45 | 46 | /// Removes a component. Error of it doesn't exist 47 | template inline void remove(); 48 | 49 | /// Removes all components and call destructors 50 | void inline remove_everything(); 51 | 52 | /// Clears the component mask without destroying components (faster than remove_everything) 53 | void inline clear_mask(); 54 | 55 | /// Destroys this entity. Removes all components as well 56 | void inline destroy(); 57 | 58 | /// Return true if entity has all specified components. False otherwise 59 | template inline bool has(); 60 | template inline bool has() const; 61 | 62 | /// Returns whether an entity is an entity alias or not 63 | template inline bool is(); 64 | template inline bool is() const; 65 | 66 | /// Returns true if entity has not been destroyed. False otherwise 67 | bool inline is_valid(); 68 | bool inline is_valid() const; 69 | 70 | private: 71 | /// Return true if entity has all specified compoents. False otherwise 72 | inline bool has(details::ComponentMask &check_mask); 73 | inline bool has(details::ComponentMask const &check_mask) const; 74 | 75 | inline details::ComponentMask &mask(); 76 | inline details::ComponentMask const &mask() const; 77 | 78 | inline static details::ComponentMask static_mask(); 79 | 80 | EntityManager *manager_; 81 | Id id_; 82 | 83 | friend class EntityManager; 84 | }; //Entity 85 | 86 | inline bool operator==(const Entity &lhs, const Entity &rhs); 87 | inline bool operator!=(const Entity &lhs, const Entity &rhs); 88 | 89 | } // namespace ecs 90 | 91 | #include "Entity.inl" 92 | 93 | #endif //ECS_ENTITY_H 94 | -------------------------------------------------------------------------------- /include/Entity.inl: -------------------------------------------------------------------------------- 1 | #include "EntityAlias.h" 2 | #include "EntityManager.h" 3 | 4 | namespace ecs{ 5 | 6 | Entity::Entity(EntityManager *manager, Id id) : 7 | manager_(manager), 8 | id_(id) 9 | { } 10 | 11 | Entity &Entity::operator=(const Entity &rhs) { 12 | manager_ = rhs.manager_; 13 | id_ = rhs.id_; 14 | return *this; 15 | } 16 | 17 | template 18 | C &Entity::get() { 19 | return manager_->get_component(*this); 20 | } 21 | 22 | template 23 | C const &Entity::get() const { 24 | return manager_->get_component(*this); 25 | } 26 | 27 | template 28 | C &Entity::set(Args && ... args){ 29 | return manager_->set_component(*this, std::forward(args) ...); 30 | } 31 | 32 | template 33 | C &Entity::add(Args && ... args){ 34 | return manager_->create_component(*this, std::forward(args) ...); 35 | } 36 | 37 | template 38 | inline T & Entity::as(){ 39 | ECS_ASSERT_IS_ENTITY(T); 40 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 41 | ECS_ASSERT(has(T::static_mask()), "Entity doesn't have required components for this EntityAlias"); 42 | return reinterpret_cast(*this); 43 | } 44 | 45 | template 46 | inline T const & Entity::as() const{ 47 | ECS_ASSERT_IS_ENTITY(T); 48 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 49 | ECS_ASSERT(has(T::static_mask()), "Entity doesn't have required components for this EntityAlias"); 50 | return reinterpret_cast(*this); 51 | } 52 | 53 | /// Assume that this entity has provided Components 54 | /// Use for faster component access calls 55 | template 56 | inline EntityAlias & Entity::assume() { 57 | return as>(); 58 | } 59 | 60 | template 61 | inline EntityAlias const & Entity::assume() const { 62 | return as>(); 63 | } 64 | 65 | template 66 | void Entity::remove() { 67 | manager_->remove_component(*this); 68 | } 69 | 70 | void Entity::remove_everything() { 71 | manager_->remove_all_components(*this); 72 | } 73 | 74 | void Entity::clear_mask() { 75 | manager_->clear_mask(*this); 76 | } 77 | 78 | void Entity::destroy() { 79 | manager_->destroy(*this); 80 | } 81 | 82 | template 83 | bool Entity::has() { 84 | return manager_->has_component(*this); 85 | } 86 | 87 | template 88 | bool Entity::has() const { 89 | return manager_->has_component(*this); 90 | } 91 | 92 | template 93 | bool Entity::is() { 94 | ECS_ASSERT_IS_ENTITY(T); 95 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 96 | return has(T::static_mask()); 97 | } 98 | 99 | template 100 | bool Entity::is() const { 101 | ECS_ASSERT_IS_ENTITY(T); 102 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 103 | return has(T::static_mask()); 104 | } 105 | 106 | bool Entity::is_valid() { 107 | return manager_->is_valid(*this); 108 | } 109 | 110 | bool Entity::is_valid() const { 111 | return manager_->is_valid(*this); 112 | } 113 | 114 | bool Entity::has(details::ComponentMask &check_mask) { 115 | return manager_->has_component(*this, check_mask); 116 | } 117 | 118 | bool Entity::has(details::ComponentMask const &check_mask) const { 119 | return manager_->has_component(*this, check_mask); 120 | } 121 | 122 | details::ComponentMask &Entity::mask() { 123 | return manager_->mask(*this); 124 | } 125 | details::ComponentMask const &Entity::mask() const { 126 | return manager_->mask(*this); 127 | } 128 | 129 | inline bool operator==(const Entity &lhs, const Entity &rhs) { 130 | return lhs.id() == rhs.id(); 131 | } 132 | 133 | inline bool operator!=(const Entity &lhs, const Entity &rhs) { 134 | return lhs.id() != rhs.id(); 135 | } 136 | 137 | details::ComponentMask Entity::static_mask(){ 138 | return details::ComponentMask(0); 139 | } 140 | 141 | } // namespace ecs -------------------------------------------------------------------------------- /include/EntityAlias.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_ENTITYALIAS_H 2 | #define ECS_ENTITYALIAS_H 3 | 4 | #include "BaseEntity.h" 5 | 6 | namespace ecs{ 7 | 8 | ///--------------------------------------------------------------------- 9 | /// EntityAlias is a wrapper around an Entity 10 | ///--------------------------------------------------------------------- 11 | /// 12 | /// An EntityAlias makes modification of the entity and other 13 | /// entities much easier when performing actions. It acts solely as an 14 | /// abstraction layer between the entity and different actions. 15 | /// 16 | ///--------------------------------------------------------------------- 17 | template 18 | class EntityAlias : public details::BaseEntity { 19 | private: 20 | /// Underlying EntityAlias. Used for creating Entity alias without 21 | /// a provided constructor 22 | using Type = EntityAlias; 23 | template 24 | using is_component = details::is_type; 25 | 26 | public: 27 | inline EntityAlias(const Entity &entity); 28 | 29 | /// Cast to Entity or EntityAlias 30 | inline operator Entity &(); 31 | inline operator Entity const &() const; 32 | 33 | inline bool operator==(const Entity &rhs) const; 34 | inline bool operator!=(const Entity &rhs) const; 35 | 36 | inline Id &id(); 37 | inline Id const &id() const; 38 | 39 | /// Returns the requested component, or error if it doesn't exist 40 | template inline auto get() -> typename std::enable_if< is_component::value, C &>::type; 41 | template inline auto get() -> typename std::enable_if::value, C &>::type; 42 | template inline auto get() const -> typename std::enable_if< is_component::value, C const &>::type; 43 | template inline auto get() const -> typename std::enable_if::value, C const &>::type; 44 | 45 | /// Set the requested component, if old component exist, 46 | /// a new one is created. Otherwise, the assignment operator 47 | /// is used. 48 | template inline auto set(Args &&... args) -> 49 | typename std::enable_if< is_component::value, C &>::type; 50 | template inline auto set(Args &&... args) -> 51 | typename std::enable_if::value, C &>::type; 52 | 53 | /// Add the requested component, error if component of the same type exist already 54 | template 55 | inline C &add(Args &&... args); 56 | 57 | /// Access this Entity as an EntityAlias. 58 | template inline T &as(); 59 | template inline T const &as() const; 60 | 61 | /// Assume that this entity has provided Components 62 | /// Use for faster component access calls 63 | template 64 | inline EntityAlias &assume(); 65 | 66 | template 67 | inline EntityAlias const &assume() const; 68 | 69 | /// Removes a component. Error of it doesn't exist. Cannot remove dependent components 70 | template inline auto remove() -> typename std::enable_if< is_component::value, void>::type; 71 | template inline auto remove() -> typename std::enable_if::value, void>::type; 72 | 73 | /// Removes all components and call destructors 74 | inline void remove_everything(); 75 | 76 | /// Clears the component mask without destroying components (faster than remove_everything) 77 | inline void clear_mask(); 78 | 79 | /// Destroys this entity. Removes all components as well 80 | inline void destroy(); 81 | /// Return true if entity has all specified components. False otherwise 82 | template inline bool has(); 83 | template inline bool has() const; 84 | 85 | /// Returns whether an entity is an entity alias or not 86 | template inline bool is(); 87 | template inline bool is() const; 88 | 89 | /// Returns true if entity has not been destroyed. False otherwise 90 | inline bool is_valid(); 91 | inline bool is_valid() const; 92 | 93 | protected: 94 | inline EntityAlias(); 95 | 96 | private: 97 | // Recursion init components with argument 98 | template 99 | inline void init_components(Arg arg) { 100 | add(arg); 101 | } 102 | 103 | template 104 | inline void init_components(Arg0 arg0, Arg1 arg1, Args... args) { 105 | init_components(arg0); 106 | init_components(arg1, args...); 107 | } 108 | // Recursion init components without argument 109 | template 110 | inline void init_components() { 111 | add(); 112 | } 113 | 114 | template 115 | inline void init_components() { 116 | init_components(); 117 | init_components(); 118 | } 119 | 120 | template 121 | inline void init(Args... args) { 122 | init_components(args...); 123 | } 124 | 125 | inline static details::ComponentMask static_mask(); 126 | 127 | friend class EntityManager; 128 | friend class Entity; 129 | }; //EntityAlias 130 | 131 | } // namespace ecs 132 | 133 | #include "EntityAlias.inl" 134 | 135 | #endif //ECS_ENTITYALIAS_H 136 | -------------------------------------------------------------------------------- /include/EntityAlias.inl: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include "EntityAlias.h" 3 | 4 | namespace ecs{ 5 | 6 | template 7 | EntityAlias::EntityAlias() { } 8 | 9 | template 10 | EntityAlias::EntityAlias(const Entity &entity) : details::BaseEntity(entity) {} 11 | 12 | 13 | template 14 | EntityAlias::operator Entity &() { 15 | return entity(); 16 | } 17 | 18 | template 19 | EntityAlias::operator Entity const &() const { 20 | return entity(); 21 | } 22 | 23 | template 24 | Id &EntityAlias::id() { 25 | return entity().id(); 26 | } 27 | 28 | template 29 | Id const &EntityAlias::id() const { 30 | return entity().id(); 31 | } 32 | 33 | template template 34 | inline auto EntityAlias::get() -> 35 | typename std::enable_if::value, C &>::type{ 36 | return entities().template get_component_fast(entity()); 37 | } 38 | 39 | template template 40 | inline auto EntityAlias::get() const -> 41 | typename std::enable_if::value, C const &>::type{ 42 | return entities().template get_component_fast(entity()); 43 | } 44 | 45 | template template 46 | inline auto EntityAlias::get() -> 47 | typename std::enable_if::value, C &>::type{ 48 | return entity().template get(); 49 | } 50 | 51 | template template 52 | inline auto EntityAlias::get() const -> 53 | typename std::enable_if::value, C const &>::type{ 54 | return entity().template get(); 55 | } 56 | 57 | template template 58 | inline auto EntityAlias::set(Args &&... args) -> 59 | typename std::enable_if::value, C &>::type{ 60 | return entities().template set_component_fast(entity(), std::forward(args)...); 61 | } 62 | 63 | template template 64 | inline auto EntityAlias::set(Args &&... args) -> 65 | typename std::enable_if::value, C &>::type{ 66 | return entities().template set_component(entity(), std::forward(args)...); 67 | } 68 | 69 | 70 | template template 71 | inline C& EntityAlias::add(Args &&... args) { 72 | return entity().template add(std::forward(args)...); 73 | } 74 | 75 | template template 76 | C & EntityAlias::as() { 77 | return entity().template as(); 78 | } 79 | 80 | template template 81 | C const & EntityAlias::as() const { 82 | return entity().template as(); 83 | } 84 | 85 | template template 86 | EntityAlias &EntityAlias::assume() { 87 | return entity().template assume(); 88 | } 89 | 90 | template template 91 | EntityAlias const &EntityAlias::assume() const { 92 | return entity().template assume(); 93 | } 94 | 95 | template template 96 | inline auto EntityAlias::remove() -> 97 | typename std::enable_if::value, void>::type { 98 | entity().template remove(); 99 | } 100 | 101 | template template 102 | inline auto EntityAlias::remove() -> 103 | typename std::enable_if::value, void>::type { 104 | entities().template remove_component_fast(entity()); 105 | } 106 | 107 | template 108 | void EntityAlias::remove_everything() { 109 | entity().remove_everything(); 110 | } 111 | 112 | template 113 | void EntityAlias::clear_mask() { 114 | entity().clear_mask(); 115 | } 116 | 117 | template 118 | void EntityAlias::destroy() { 119 | entity().destroy(); 120 | } 121 | 122 | template template 123 | bool EntityAlias::has() { 124 | return entity().template has(); 125 | } 126 | 127 | template template 128 | bool EntityAlias::has() const { 129 | return entity().template has(); 130 | } 131 | 132 | template template 133 | bool EntityAlias::is() { 134 | return entity().template is(); 135 | } 136 | template template 137 | bool EntityAlias::is() const { 138 | return entity().template is(); 139 | } 140 | 141 | template 142 | bool EntityAlias::is_valid() { 143 | return entity().is_valid(); 144 | } 145 | 146 | template 147 | bool EntityAlias::is_valid() const { 148 | return entity().is_valid(); 149 | } 150 | 151 | template 152 | details::ComponentMask EntityAlias::static_mask(){ 153 | return details::component_mask(); 154 | } 155 | 156 | template 157 | inline bool EntityAlias::operator==(const Entity &rhs) const { 158 | return entity() == rhs; 159 | } 160 | 161 | template 162 | inline bool EntityAlias::operator!=(const Entity &rhs) const { 163 | return entity() != rhs; 164 | } 165 | 166 | 167 | } // namespace ecs -------------------------------------------------------------------------------- /include/EntityManager.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_ENTITYMANAGER_H 2 | #define ECS_ENTITYMANAGER_H 3 | 4 | namespace ecs { 5 | 6 | /// Forward declareations 7 | template 8 | class EntityAlias; 9 | class Entity; 10 | class UnallocatedEntity; 11 | template 12 | class View; 13 | class Id; 14 | 15 | namespace details{ 16 | 17 | /// Used to iterate through the EntityManager with a lambda-expression, 18 | /// with each argument as a specific component type. Used by with 19 | /// function. 20 | template 21 | struct with_t; 22 | 23 | } // namespace details 24 | 25 | ///--------------------------------------------------------------------- 26 | /// This is the main class for holding all Entities and Components 27 | ///--------------------------------------------------------------------- 28 | /// 29 | /// It uses a vector for holding entity versions and another for component 30 | /// masks. It also uses 1 memory pool for each type of component. 31 | /// 32 | /// The index of each data structure is used to identify each entity. 33 | /// The version is used for checking if entity is valid. 34 | /// The component mask is used to check what components an entity has 35 | /// 36 | /// The Entity id = {index + version}. When an Entity is removed, 37 | /// the index is added to the free list. A free list knows where spaces are 38 | /// left open for new entities (to ensure no holes). 39 | /// 40 | ///--------------------------------------------------------------------- 41 | class EntityManager: details::forbid_copies { 42 | private: 43 | // Class for accessing where to put entities with specific components. 44 | struct IndexAccessor { 45 | // Used to store next available index there is within each block 46 | std::vector block_index; 47 | // Used to store all indexes that are free 48 | std::vector free_list; 49 | }; 50 | 51 | public: 52 | inline EntityManager(size_t chunk_size = 8192); 53 | inline ~EntityManager(); 54 | 55 | /// Create a new Entity 56 | inline UnallocatedEntity create(); 57 | 58 | /// Create a specified number of new entities. 59 | inline std::vector create(const size_t num_of_entities); 60 | 61 | /// Create a specified number of new entities, and do something with each entity 62 | template 63 | inline void create(const size_t num_of_entities, T lambda); 64 | 65 | /// Create, using EntityAlias 66 | template 67 | inline auto create(Args &&... args) -> typename std::enable_if::value, T>::type; 68 | template 69 | inline auto create(Args &&... args) -> typename std::enable_if::value, T>::type; 70 | 71 | /// Create an entity with components assigned 72 | template 73 | inline auto create_with(Args && ... args ) -> 74 | typename std::conditional<(sizeof...(Components) > 0), EntityAlias, EntityAlias>::type; 75 | 76 | /// Create an entity with components assigned, using default values 77 | template 78 | inline EntityAlias create_with(); 79 | 80 | // Access a View of all entities with specified components 81 | template 82 | inline View > with(); 83 | 84 | // Iterate through all entities with all components, specified as lambda parameters 85 | // example: entities.with([] (Position& pos) { }); 86 | template 87 | inline void with(T lambda); 88 | 89 | // Access a View of all entities that has every component as Specified EntityAlias 90 | template 91 | inline View fetch_every(); 92 | 93 | // Access a View of all entities that has every component as Specified EntityAlias specified as lambda parameters 94 | // example: entities.fetch_every([] (EntityAlias& entity) { }); 95 | template 96 | inline void fetch_every(T lambda); 97 | 98 | // Get an Entity at specified index 99 | inline Entity operator[](index_t index); 100 | 101 | // Get an Entity with a specific Id. Id must be valid 102 | inline Entity operator[](Id id); 103 | 104 | // Get the Entity count for this EntityManager 105 | inline size_t count(); 106 | 107 | private: 108 | 109 | /// Creates an entity and put it close to entities 110 | /// with similar components 111 | inline Entity create_with_mask(details::ComponentMask mask); 112 | 113 | /// Creates any number of entities > 0 and put them close to entities 114 | /// with similar components 115 | inline std::vector create_with_mask(details::ComponentMask mask, const size_t num_of_entities); 116 | 117 | /// Find a proper index for a new entity with components 118 | inline index_t find_new_entity_index(details::ComponentMask mask); 119 | 120 | /// Create a new block for this entity type. 121 | inline void create_new_block(IndexAccessor &index_accessor, unsigned long mask_as_ulong, index_t next_free_index); 122 | 123 | /// Creates a ComponentManager. Mainly used by get_component_manager the first time its called 124 | template 125 | inline details::ComponentManager &create_component_manager(Args &&... args); 126 | 127 | /// Get the ComponentManager. Assumes that the component manager already exists. 128 | template 129 | inline details::ComponentManager &get_component_manager_fast(); 130 | template 131 | inline details::ComponentManager const &get_component_manager_fast() const; 132 | 133 | /// Get the ComponentManager. Creates a component manager if it 134 | /// doesn't exists for specified component type. 135 | template 136 | inline details::ComponentManager &get_component_manager(); 137 | template 138 | inline details::ComponentManager const &get_component_manager() const; 139 | 140 | inline details::BaseManager &get_component_manager(size_t component_index); 141 | inline details::BaseManager const &get_component_manager(size_t component_index) const; 142 | 143 | /// Get component for a specific entity or index. 144 | template 145 | inline C &get_component(Entity &entity); 146 | template 147 | inline C const &get_component(Entity const &entity) const; 148 | 149 | /// Get component for a specific entity or index. Assumes that a 150 | /// ComponentManager exists for the specific component type. 151 | template 152 | inline C &get_component_fast(index_t index); 153 | template 154 | inline C const &get_component_fast(index_t index) const; 155 | template 156 | inline C &get_component_fast(Entity &entity); 157 | template 158 | inline C const &get_component_fast(Entity const &entity) const; 159 | 160 | /// Use to create a component tmp that is assignable. Calls the constructor. 161 | template 162 | inline static auto create_tmp_component(Args &&... args) -> 163 | typename std::enable_if::value, C>::type { 164 | return C(std::forward(args) ...); 165 | } 166 | 167 | /// Use to create a component tmp that is assignable. Uses uniform initialization. 168 | template 169 | inline static auto create_tmp_component(Args &&... args) -> 170 | typename std::enable_if< 171 | !std::is_constructible::value && 172 | !std::is_base_of::value, 173 | C>::type { 174 | return C{std::forward(args) ...}; 175 | } 176 | 177 | /// Use to create a component tmp that is assignable. Call the right constructor 178 | /// Called when component is a property, and no constructor inaccessible. 179 | template 180 | inline static auto create_tmp_component(Args &&... args) -> 181 | typename std::enable_if< 182 | !std::is_constructible::value && 183 | std::is_base_of::value, 184 | C>::type { 185 | static_assert(sizeof...(Args) == 1, ECS_ASSERT_MSG_ONLY_ONE_ARGS_PROPERTY_CONSTRUCTOR); 186 | //static_assert(sizeof(C) == sizeof(std::tuple < Args... > ), 187 | //"Cannot initilize component property. Please provide a constructor"); 188 | auto tmp = typename C::ValueType(std::forward(args)...); 189 | return *reinterpret_cast(&tmp); 190 | } 191 | 192 | /// Creates a component for a specific entity with Args 193 | template 194 | inline C &create_component(Entity &entity, Args &&... args); 195 | 196 | /// Removes a specific component from an entity. 197 | template 198 | inline void remove_component(Entity &entity); 199 | 200 | /// Removes a specific component from an entity. Assumes that 201 | /// a ComponentManager exists for component type 202 | template 203 | inline void remove_component_fast(Entity &entity); 204 | 205 | /// Removes all components from a single entity 206 | inline void remove_all_components(Entity &entity); 207 | 208 | /// Clears the component mask without removing any components 209 | inline void clear_mask(Entity &entity); 210 | 211 | /// Set a component for a specific entity. It uses contructor if 212 | /// entity does not have component. It uses assignment operator 213 | /// if component already added 214 | template 215 | inline C &set_component(Entity &entity, Args &&... args); 216 | 217 | /// Set a component for a specific entity. It assumes that the 218 | /// entity already has the component and uses the assignment 219 | /// operator. 220 | template 221 | inline C &set_component_fast(Entity &entity, Args &&... args); 222 | 223 | /// Check if component has specified component types attached 224 | inline bool has_component(Entity &entity, details::ComponentMask component_mask); 225 | inline bool has_component(Entity const &entity, details::ComponentMask const &component_mask) const; 226 | template 227 | inline bool has_component(Entity &entity); 228 | template 229 | inline bool has_component(Entity const &entity) const; 230 | 231 | /// Check if an entity is valid 232 | inline bool is_valid(Entity &entity); 233 | inline bool is_valid(Entity const &entity) const; 234 | 235 | /// Destroy an entity. Also removed all added components 236 | inline void destroy(Entity &entity); 237 | 238 | /// Get the entity mask from a specific entity or index 239 | inline details::ComponentMask &mask(Entity &entity); 240 | inline details::ComponentMask const &mask(Entity const &entity) const; 241 | inline details::ComponentMask &mask(index_t index); 242 | inline details::ComponentMask const &mask(index_t index) const; 243 | 244 | /// Get an entity given its id or index. When using Id, it must always be valid 245 | inline Entity get_entity(Id id); 246 | inline Entity get_entity(index_t index); 247 | 248 | /// Gey how many entities the EntityManager can handle atm 249 | inline size_t capacity() const; 250 | 251 | std::vector component_managers_; 252 | std::vector component_masks_; 253 | std::vector entity_versions_; 254 | std::vector next_free_indexes_; 255 | std::vector index_to_component_mask; 256 | std::map component_mask_to_index_accessor_; 257 | 258 | /// How many blocks of entities there exists. Used when tracking where to put entities in momory 259 | index_t block_count_ = 0; 260 | /// How many entities there are atm 261 | index_t count_ = 0; 262 | 263 | /// The EntityManager want some friends :) 264 | template 265 | friend struct details::with_t; 266 | template 267 | friend class details::ComponentManager; 268 | template 269 | friend class EntityAlias; 270 | template 271 | friend class Iterator; 272 | friend class Entity; 273 | friend class UnallocatedEntity; 274 | friend class BaseComponent; 275 | }; 276 | 277 | } // namespace ecs 278 | 279 | #include "EntityManager.inl" 280 | 281 | #endif //ECS_ENTITYMANAGER_H 282 | -------------------------------------------------------------------------------- /include/EntityManager.inl: -------------------------------------------------------------------------------- 1 | #include "Entity.h" 2 | #include "UnallocatedEntity.h" 3 | 4 | namespace ecs{ 5 | 6 | namespace details{ 7 | 8 | template 9 | struct with_t: 10 | with_t::template arg_remove_ref, Args...> { 12 | }; 13 | 14 | template 15 | struct with_t<0, Lambda, Args...> { 16 | static inline void for_each(EntityManager &manager, Lambda lambda) { 17 | typedef details::function_traits function; 18 | static_assert(function::arg_count > 0, "Lambda or function must have at least 1 argument."); 19 | auto view = manager.with(); 20 | auto it = view.begin(); 21 | auto end = view.end(); 22 | for (; it != end; ++it) { 23 | lambda(get_arg(manager, it.index())...); 24 | } 25 | } 26 | 27 | //When arg is component, access component 28 | template 29 | static inline auto get_arg(EntityManager &manager, index_t index) -> 30 | typename std::enable_if::value, C &>::type { 31 | return manager.get_component_fast(index); 32 | } 33 | 34 | //When arg is the Entity, access the Entity 35 | template 36 | static inline auto get_arg(EntityManager &manager, index_t index) -> 37 | typename std::enable_if::value, Entity>::type { 38 | return manager.get_entity(index); 39 | } 40 | }; 41 | 42 | template 43 | using with_ = with_t::arg_count, Lambda>; 44 | 45 | } // namespace details 46 | 47 | EntityManager::EntityManager(size_t chunk_size) { 48 | entity_versions_.reserve(chunk_size); 49 | component_masks_.reserve(chunk_size); 50 | } 51 | 52 | 53 | EntityManager::~EntityManager() { 54 | for (details::BaseManager *manager : component_managers_) { 55 | if (manager) delete manager; 56 | } 57 | component_managers_.clear(); 58 | component_masks_.clear(); 59 | entity_versions_.clear(); 60 | next_free_indexes_.clear(); 61 | component_mask_to_index_accessor_.clear(); 62 | index_to_component_mask.clear(); 63 | } 64 | 65 | UnallocatedEntity EntityManager::create() { 66 | return UnallocatedEntity(*this); 67 | } 68 | 69 | 70 | std::vector EntityManager::create(const size_t num_of_entities) { 71 | return create_with_mask(details::ComponentMask(0), num_of_entities); 72 | } 73 | 74 | template 75 | inline void EntityManager::create(const size_t num_of_entities, T lambda){ 76 | ECS_ASSERT_IS_CALLABLE(T); 77 | using EntityAlias_ = typename details::function_traits::template arg_remove_ref<0>; 78 | ECS_ASSERT_IS_ENTITY(EntityAlias_); 79 | for(Entity& entity : create_with_mask(EntityAlias_::static_mask(), num_of_entities)){ 80 | //We cannot use as since we dont have attached any components 81 | lambda(*reinterpret_cast(&entity)); 82 | ECS_ASSERT(entity.has(EntityAlias_::static_mask()), "Entity are missing certain components."); 83 | } 84 | } 85 | 86 | /// If EntityAlias is constructable with Args... 87 | template 88 | auto EntityManager::create(Args && ... args) -> 89 | typename std::enable_if::value, T>::type { 90 | ECS_ASSERT_IS_ENTITY(T); 91 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 92 | auto mask = T::static_mask(); 93 | Entity entity = create_with_mask(mask); 94 | T *entity_alias = new(&entity) T(std::forward(args)...); 95 | ECS_ASSERT(entity.has(mask), 96 | "Every required component must be added when creating an Entity Alias"); 97 | return *entity_alias; 98 | } 99 | 100 | /// If EntityAlias is not constructable with Args... 101 | /// Attempt to create with underlying EntityAlias 102 | template 103 | auto EntityManager::create(Args && ... args) -> 104 | typename std::enable_if::value, T>::type { 105 | ECS_ASSERT_IS_ENTITY(T); 106 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 107 | typedef typename T::Type Type; 108 | auto mask = T::static_mask(); 109 | Entity entity = create_with_mask(mask); 110 | Type *entity_alias = new(&entity) Type(); 111 | entity_alias->init(std::forward(args)...); 112 | ECS_ASSERT(entity.has(mask), 113 | "Every required component must be added when creating an Entity Alias"); 114 | return *reinterpret_cast(entity_alias); 115 | } 116 | 117 | template 118 | auto EntityManager::create_with(Args && ... args ) -> 119 | typename std::conditional<(sizeof...(Components) > 0), EntityAlias, EntityAlias>::type{ 120 | using Type = typename std::conditional<(sizeof...(Components) > 0), EntityAlias, EntityAlias>::type; 121 | Entity entity = create_with_mask(Type::static_mask()); 122 | Type *entity_alias = new(&entity) Type(); 123 | entity_alias->init(std::forward(args)...); 124 | return *entity_alias; 125 | } 126 | 127 | template 128 | EntityAlias EntityManager::create_with() { 129 | using Type = EntityAlias; 130 | Entity entity = create_with_mask(details::component_mask()); 131 | Type *entity_alias = new(&entity) Type(); 132 | entity_alias->init(); 133 | return *entity_alias; 134 | } 135 | 136 | Entity EntityManager::create_with_mask(details::ComponentMask mask) { 137 | ++count_; 138 | index_t index = find_new_entity_index(mask); 139 | size_t slots_required = index + 1; 140 | if (entity_versions_.size() < slots_required) { 141 | entity_versions_.resize(slots_required); 142 | component_masks_.resize(slots_required, details::ComponentMask(0)); 143 | } 144 | return get_entity(index); 145 | } 146 | 147 | std::vector EntityManager::create_with_mask(details::ComponentMask mask, const size_t num_of_entities) { 148 | std::vector new_entities; 149 | size_t entities_left = num_of_entities; 150 | new_entities.reserve(entities_left); 151 | auto mask_as_ulong = mask.to_ulong(); 152 | IndexAccessor &index_accessor = component_mask_to_index_accessor_[mask_as_ulong]; 153 | //See if we can use old indexes for destroyed entities via free list 154 | while (!index_accessor.free_list.empty() && entities_left--) { 155 | new_entities.push_back(get_entity(index_accessor.free_list.back())); 156 | index_accessor.free_list.pop_back(); 157 | } 158 | index_t block_index = 0; 159 | index_t current = ECS_CACHE_LINE_SIZE; // <- if empty, create new block instantly 160 | size_t slots_required; 161 | // Are there any unfilled blocks? 162 | if (!index_accessor.block_index.empty()) { 163 | block_index = index_accessor.block_index[index_accessor.block_index.size() - 1]; 164 | current = next_free_indexes_[block_index]; 165 | slots_required = block_count_ * ECS_CACHE_LINE_SIZE + current + entities_left; 166 | } else { 167 | slots_required = block_count_ * ECS_CACHE_LINE_SIZE + entities_left; 168 | } 169 | entity_versions_.resize(slots_required); 170 | component_masks_.resize(slots_required, details::ComponentMask(0)); 171 | 172 | // Insert until no entity is left or no block remain 173 | while (entities_left) { 174 | for (; current < ECS_CACHE_LINE_SIZE && entities_left; ++current) { 175 | new_entities.push_back(get_entity(current + ECS_CACHE_LINE_SIZE * block_index)); 176 | entities_left--; 177 | } 178 | // Add more blocks if there are entities left 179 | if (entities_left) { 180 | block_index = block_count_; 181 | create_new_block(index_accessor, mask_as_ulong, 0); 182 | ++block_count_; 183 | current = 0; 184 | } 185 | } 186 | count_ += num_of_entities; 187 | return new_entities; 188 | } 189 | 190 | template 191 | View> EntityManager::with() { 192 | details::ComponentMask mask = details::component_mask(); 193 | return View>(this, mask); 194 | } 195 | 196 | template 197 | void EntityManager::with(T lambda) { 198 | ECS_ASSERT_IS_CALLABLE(T); 199 | details::with_::for_each(*this, lambda); 200 | } 201 | 202 | 203 | template 204 | View EntityManager::fetch_every() { 205 | ECS_ASSERT_IS_ENTITY(T); 206 | return View(this, T::static_mask()); 207 | } 208 | 209 | 210 | template 211 | void EntityManager::fetch_every(T lambda) { 212 | ECS_ASSERT_IS_CALLABLE(T); 213 | typedef details::function_traits function; 214 | static_assert(function::arg_count == 1, "Lambda or function must only have one argument"); 215 | typedef typename function::template arg_remove_ref<0> entity_interface_t; 216 | for (entity_interface_t entityInterface : fetch_every()) { 217 | lambda(entityInterface); 218 | } 219 | } 220 | 221 | 222 | Entity EntityManager::operator[](index_t index) { 223 | return get_entity(index); 224 | } 225 | 226 | 227 | Entity EntityManager::operator[](Id id) { 228 | Entity entity = get_entity(id); 229 | ECS_ASSERT(id == entity.id(), "Id is no longer valid (Entity was destroyed)"); 230 | return entity; 231 | } 232 | 233 | size_t EntityManager::count(){ 234 | return count_; 235 | } 236 | 237 | index_t EntityManager::find_new_entity_index(details::ComponentMask mask) { 238 | auto mask_as_ulong = mask.to_ulong(); 239 | IndexAccessor &index_accessor = component_mask_to_index_accessor_[mask_as_ulong]; 240 | //See if we can use old indexes for destroyed entities via free list 241 | if (!index_accessor.free_list.empty()) { 242 | auto index = index_accessor.free_list.back(); 243 | index_accessor.free_list.pop_back(); 244 | return index; 245 | } 246 | // EntityManager has created similar entities already 247 | if (!index_accessor.block_index.empty()) { 248 | //No free_indexes in free list (removed entities), find a new index 249 | //at last block, if that block has free slots 250 | auto &block_index = index_accessor.block_index[index_accessor.block_index.size() - 1]; 251 | auto ¤t = next_free_indexes_[block_index]; 252 | // If block has empty slot, use it 253 | if (ECS_CACHE_LINE_SIZE > current) { 254 | return (current++) + ECS_CACHE_LINE_SIZE * block_index; 255 | } 256 | } 257 | create_new_block(index_accessor, mask_as_ulong, 1); 258 | return (block_count_++) * ECS_CACHE_LINE_SIZE; 259 | } 260 | 261 | void EntityManager::create_new_block(EntityManager::IndexAccessor &index_accessor, 262 | unsigned long mask_as_ulong, 263 | index_t next_free_index) { 264 | index_accessor.block_index.push_back(block_count_); 265 | next_free_indexes_.resize(block_count_ + 1); 266 | index_to_component_mask.resize(block_count_ + 1); 267 | next_free_indexes_[block_count_] = next_free_index; 268 | index_to_component_mask[block_count_] = mask_as_ulong; 269 | } 270 | 271 | template 272 | details::ComponentManager &EntityManager::create_component_manager(Args && ... args) { 273 | details::ComponentManager *ptr = new details::ComponentManager(std::forward(*this), 274 | std::forward(args) ...); 275 | component_managers_[details::component_index()] = ptr; 276 | return *ptr; 277 | } 278 | 279 | template 280 | details::ComponentManager &EntityManager::get_component_manager_fast() { 281 | return *reinterpret_cast *>(component_managers_[details::component_index()]); 282 | } 283 | 284 | template 285 | details::ComponentManager const &EntityManager::get_component_manager_fast() const { 286 | return *reinterpret_cast *>(component_managers_[details::component_index()]); 287 | } 288 | 289 | 290 | template 291 | details::ComponentManager &EntityManager::get_component_manager() { 292 | auto index = details::component_index(); 293 | if (component_managers_.size() <= index) { 294 | component_managers_.resize(index + 1, nullptr); 295 | return create_component_manager(); 296 | } else if (component_managers_[index] == nullptr) { 297 | return create_component_manager(); 298 | } 299 | return *reinterpret_cast *>(component_managers_[index]); 300 | } 301 | 302 | 303 | template 304 | details::ComponentManager const &EntityManager::get_component_manager() const { 305 | auto index = details::component_index(); 306 | ECS_ASSERT(component_managers_.size() > index, component_managers_[index] == nullptr && 307 | "Component manager not created"); 308 | return *reinterpret_cast *>(component_managers_[index]); 309 | } 310 | 311 | details::BaseManager &EntityManager::get_component_manager(size_t component_index){ 312 | ECS_ASSERT(component_managers_.size() > component_index, "ComponentManager not created with that component index."); 313 | return *component_managers_[component_index]; 314 | } 315 | details::BaseManager const &EntityManager::get_component_manager(size_t component_index) const{ 316 | ECS_ASSERT(component_managers_.size() > component_index, "ComponentManager not created with that component index."); 317 | return *component_managers_[component_index]; 318 | } 319 | 320 | template 321 | C &EntityManager::get_component(Entity &entity) { 322 | ECS_ASSERT(has_component(entity), "Entity doesn't have this component attached"); 323 | return get_component_manager().get(entity.id_.index_); 324 | } 325 | 326 | template 327 | C const &EntityManager::get_component(Entity const &entity) const { 328 | ECS_ASSERT(has_component(entity), "Entity doesn't have this component attached"); 329 | return get_component_manager().get(entity.id_.index_); 330 | } 331 | 332 | template 333 | C &EntityManager::get_component_fast(index_t index) { 334 | return get_component_manager_fast().get(index); 335 | } 336 | 337 | template 338 | C const &EntityManager::get_component_fast(index_t index) const { 339 | return get_component_manager_fast().get(index); 340 | } 341 | 342 | 343 | template 344 | C &EntityManager::get_component_fast(Entity &entity) { 345 | return get_component_manager_fast().get(entity.id_.index_); 346 | } 347 | 348 | template 349 | C const &EntityManager::get_component_fast(Entity const &entity) const { 350 | return get_component_manager_fast().get(entity.id_.index_); 351 | } 352 | 353 | template 354 | C &EntityManager::create_component(Entity &entity, Args && ... args) { 355 | ECS_ASSERT_VALID_ENTITY(entity); 356 | ECS_ASSERT(!has_component(entity), "Entity already has this component attached"); 357 | C &component = get_component_manager().create(entity.id_.index_, std::forward(args) ...); 358 | entity.mask().set(details::component_index()); 359 | return component; 360 | } 361 | 362 | template 363 | void EntityManager::remove_component(Entity &entity) { 364 | ECS_ASSERT_VALID_ENTITY(entity); 365 | ECS_ASSERT(has_component(entity), "Entity doesn't have component attached"); 366 | get_component_manager().remove(entity.id_.index_); 367 | } 368 | 369 | template 370 | void EntityManager::remove_component_fast(Entity &entity) { 371 | ECS_ASSERT_VALID_ENTITY(entity); 372 | ECS_ASSERT(has_component(entity), "Entity doesn't have component attached"); 373 | get_component_manager_fast().remove(entity.id_.index_); 374 | } 375 | 376 | void EntityManager::remove_all_components(Entity &entity) { 377 | ECS_ASSERT_VALID_ENTITY(entity); 378 | for (auto componentManager : component_managers_) { 379 | if (componentManager && has_component(entity, componentManager->mask())) { 380 | componentManager->remove(entity.id_.index_); 381 | } 382 | } 383 | } 384 | 385 | void EntityManager::clear_mask(Entity &entity) { 386 | ECS_ASSERT_VALID_ENTITY(entity); 387 | component_masks_[entity.id_.index_].reset(); 388 | } 389 | 390 | template 391 | C &EntityManager::set_component(Entity &entity, Args && ... args) { 392 | ECS_ASSERT_VALID_ENTITY(entity); 393 | if (entity.has()) { 394 | return get_component_fast(entity) = create_tmp_component(std::forward(args)...); 395 | } 396 | else return create_component(entity, std::forward(args)...); 397 | } 398 | 399 | template 400 | C &EntityManager::set_component_fast(Entity &entity, Args && ... args) { 401 | ECS_ASSERT_VALID_ENTITY(entity); 402 | ECS_ASSERT(entity.has(), "Entity does not have component attached"); 403 | return get_component_fast(entity) = create_tmp_component(std::forward(args)...); 404 | } 405 | 406 | bool EntityManager::has_component(Entity &entity, details::ComponentMask component_mask) { 407 | ECS_ASSERT_VALID_ENTITY(entity); 408 | return (mask(entity) & component_mask) == component_mask; 409 | } 410 | 411 | bool EntityManager::has_component(Entity const &entity, details::ComponentMask const &component_mask) const { 412 | ECS_ASSERT_VALID_ENTITY(entity); 413 | return (mask(entity) & component_mask) == component_mask; 414 | } 415 | 416 | template 417 | bool EntityManager::has_component(Entity &entity) { 418 | return has_component(entity, details::component_mask()); 419 | } 420 | 421 | template 422 | bool EntityManager::has_component(Entity const &entity) const { 423 | return has_component(entity, details::component_mask()); 424 | } 425 | 426 | 427 | bool EntityManager::is_valid(Entity &entity) { 428 | return entity.id_.index_ < entity_versions_.size() && 429 | entity.id_.version_ == entity_versions_[entity.id_.index_]; 430 | } 431 | 432 | bool EntityManager::is_valid(Entity const &entity) const { 433 | return entity.id_.index_ < entity_versions_.size() && 434 | entity.id_.version_ == entity_versions_[entity.id_.index_]; 435 | } 436 | 437 | void EntityManager::destroy(Entity &entity) { 438 | index_t index = entity.id().index_; 439 | remove_all_components(entity); 440 | ++entity_versions_[index]; 441 | auto &mask_as_ulong = index_to_component_mask[index / ECS_CACHE_LINE_SIZE]; 442 | component_mask_to_index_accessor_[mask_as_ulong].free_list.push_back(index); 443 | --count_; 444 | } 445 | 446 | details::ComponentMask &EntityManager::mask(Entity &entity) { 447 | return mask(entity.id_.index_); 448 | } 449 | 450 | details::ComponentMask const &EntityManager::mask(Entity const &entity) const { 451 | return mask(entity.id_.index_); 452 | } 453 | 454 | details::ComponentMask &EntityManager::mask(index_t index) { 455 | return component_masks_[index]; 456 | } 457 | 458 | details::ComponentMask const &EntityManager::mask(index_t index) const { 459 | return component_masks_[index]; 460 | } 461 | 462 | Entity EntityManager::get_entity(Id id) { 463 | return Entity(this, id); 464 | } 465 | 466 | Entity EntityManager::get_entity(index_t index) { 467 | return get_entity(Id(index, entity_versions_[index])); 468 | } 469 | 470 | size_t EntityManager::capacity() const { 471 | return entity_versions_.capacity(); 472 | } 473 | 474 | } // namespace ecs -------------------------------------------------------------------------------- /include/Id.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_ID_H 2 | #define ECS_ID_H 3 | 4 | #include "Defines.h" 5 | 6 | namespace ecs{ 7 | 8 | ///--------------------------------------------------------------------- 9 | /// Id is used for Entity to identify entities. It consists of an index 10 | /// and a version. The index describes where the entity is located in 11 | /// memory. The version is used to separate entities if they get the 12 | /// same index. 13 | ///--------------------------------------------------------------------- 14 | class Id { 15 | public: 16 | inline Id(); 17 | inline Id(index_t index, version_t version); 18 | 19 | inline index_t index() { return index_; } 20 | inline index_t index() const { return index_; } 21 | 22 | inline version_t version() { return version_; } 23 | inline version_t version() const { return version_; } 24 | 25 | private: 26 | index_t index_; 27 | version_t version_; 28 | friend class Entity; 29 | friend class EntityManager; 30 | }; 31 | 32 | inline bool operator==(const Id& lhs, const Id &rhs); 33 | inline bool operator!=(const Id& lhs, const Id &rhs); 34 | 35 | } // namespace ecs 36 | 37 | #include "Id.inl" 38 | 39 | #endif //ECS_ID_H 40 | -------------------------------------------------------------------------------- /include/Id.inl: -------------------------------------------------------------------------------- 1 | namespace ecs{ 2 | 3 | Id::Id() { } 4 | 5 | Id::Id(index_t index, version_t version) : 6 | index_(index), 7 | version_(version) 8 | { } 9 | 10 | bool operator==(const Id& lhs, const Id &rhs) { 11 | return lhs.index() == rhs.index() && lhs.version() == rhs.version(); 12 | } 13 | 14 | bool operator!=(const Id& lhs, const Id &rhs) { 15 | return lhs.index() != rhs.index() || lhs.version() != rhs.version(); 16 | } 17 | 18 | } // namespace ecs -------------------------------------------------------------------------------- /include/Iterator.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_ITERATOR_H 2 | #define ECS_ITERATOR_H 3 | 4 | namespace ecs{ 5 | 6 | ///--------------------------------------------------------------------- 7 | /// Iterator is an iterator for iterating through the entity manager. 8 | /// The iterator should fulfill the c++ standards for an input iterator, 9 | ///--------------------------------------------------------------------- 10 | template 11 | class Iterator: public std::iterator::type> { 12 | using T_no_ref = typename std::remove_reference::type>::type; 13 | 14 | public: 15 | Iterator(EntityManager *manager, details::ComponentMask mask, bool begin = true); 16 | Iterator(const Iterator &it); 17 | Iterator &operator=(const Iterator &rhs) = default; 18 | 19 | // Get the current index (cursor) 20 | index_t index() const; 21 | Iterator &operator++(); 22 | 23 | // The the current entity 24 | T entity(); 25 | T const entity() const; 26 | 27 | private: 28 | // find next entity withing the EntityManager which has the correct components 29 | void find_next(); 30 | 31 | EntityManager *manager_; 32 | details::ComponentMask mask_; 33 | index_t cursor_; 34 | size_t size_; 35 | }; //Iterator 36 | 37 | template bool operator==(Iterator const &lhs, Iterator const &rhs); 38 | template bool operator!=(Iterator const &lhs, Iterator const &rhs); 39 | template inline T operator*(Iterator &lhs); 40 | template inline T const &operator*(Iterator const &lhs); 41 | 42 | } // namespace ecs 43 | 44 | #include "Iterator.inl" 45 | 46 | #endif //ECS_ITERATOR_H 47 | -------------------------------------------------------------------------------- /include/Iterator.inl: -------------------------------------------------------------------------------- 1 | namespace ecs{ 2 | 3 | 4 | template 5 | Iterator::Iterator(EntityManager *manager, details::ComponentMask mask, bool begin) : 6 | manager_(manager), 7 | mask_(mask), 8 | cursor_(0){ 9 | // Must be pool size because of potential holes 10 | size_ = manager_->entity_versions_.size(); 11 | if (!begin) cursor_ = size_; 12 | find_next(); 13 | } 14 | 15 | template 16 | Iterator::Iterator(const Iterator &it) : Iterator(it.manager_, it.cursor_) { } 17 | 18 | template 19 | index_t Iterator::index() const { 20 | return cursor_; 21 | } 22 | 23 | template 24 | inline void Iterator::find_next() { 25 | while (cursor_ < size_ && (manager_->component_masks_[cursor_] & mask_) != mask_) { 26 | ++cursor_; 27 | } 28 | } 29 | 30 | template 31 | T Iterator::entity() { 32 | return manager_->get_entity(index()).template as::T_no_ref>(); 33 | } 34 | 35 | template 36 | const T Iterator::entity() const { 37 | return manager_->get_entity(index()).template as::T_no_ref>(); 38 | } 39 | 40 | template 41 | Iterator &Iterator::operator++() { 42 | ++cursor_; 43 | find_next(); 44 | return *this; 45 | } 46 | 47 | template 48 | bool operator==(Iterator const &lhs, Iterator const &rhs) { 49 | return lhs.index() == rhs.index(); 50 | } 51 | 52 | template 53 | bool operator!=(Iterator const &lhs, Iterator const &rhs) { 54 | return !(lhs == rhs); 55 | } 56 | 57 | template 58 | inline T operator*(Iterator &lhs) { 59 | return lhs.entity(); 60 | } 61 | 62 | template 63 | inline T const &operator*(Iterator const &lhs) { 64 | return lhs.entity(); 65 | } 66 | 67 | 68 | } // namespace ecs -------------------------------------------------------------------------------- /include/Pool.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_POOL_H 2 | #define ECS_POOL_H 3 | 4 | #include "Utils.h" 5 | 6 | namespace ecs{ 7 | 8 | namespace details { 9 | 10 | ///--------------------------------------------------------------------- 11 | /// A Pool is a memory pool. 12 | /// See link for more info: http://en.wikipedia.org/wiki/Memory_pool 13 | ///--------------------------------------------------------------------- 14 | /// 15 | /// The BasePool is used to store void*. Use Pool for a generic 16 | /// Pool allocation class. The standard is to store one cache-line 17 | /// (64 bytes) per chunk. 18 | /// 19 | ///--------------------------------------------------------------------- 20 | class BasePool: forbid_copies { 21 | public: 22 | inline explicit BasePool(size_t element_size, size_t chunk_size = ECS_DEFAULT_CHUNK_SIZE); 23 | inline virtual ~BasePool(); 24 | 25 | inline index_t size() const { return size_; } 26 | inline index_t capacity() const { return capacity_; } 27 | inline size_t chunks() const { return chunks_.size(); } 28 | inline void ensure_min_size(std::size_t size); 29 | inline void ensure_min_capacity(size_t min_capacity); 30 | 31 | virtual void destroy(index_t index) = 0; 32 | 33 | protected: 34 | index_t size_; 35 | index_t capacity_; 36 | size_t element_size_; 37 | size_t chunk_size_; 38 | std::vector chunks_; 39 | }; 40 | 41 | ///--------------------------------------------------------------------- 42 | /// A Pool is a memory pool. 43 | /// See link for more info: http://en.wikipedia.org/wiki/Memory_pool 44 | ///--------------------------------------------------------------------- 45 | /// 46 | /// The Pool is used to store * of any class. Destroying an object calls 47 | /// destructor. The pool doesn't know where there is data allocated. 48 | /// This must be done from outside. The default chunk-size is 64 bytes * 49 | /// the size of each object. 50 | /// 51 | ///--------------------------------------------------------------------- 52 | template 53 | class Pool: public BasePool { 54 | public: 55 | Pool(size_t chunk_size); 56 | 57 | inline virtual void destroy(index_t index) override; 58 | 59 | inline T *get_ptr(index_t index); 60 | inline const T *get_ptr(index_t index) const; 61 | 62 | inline T &get(index_t index); 63 | inline const T &get(index_t index) const; 64 | 65 | inline T &operator[](size_t index); 66 | inline const T &operator[](size_t index) const; 67 | }; 68 | 69 | } // namespace details 70 | 71 | } // namespace ecs 72 | 73 | #include "Pool.inl" 74 | 75 | #endif //ECS_POOL_H 76 | -------------------------------------------------------------------------------- /include/Pool.inl: -------------------------------------------------------------------------------- 1 | namespace ecs{ 2 | 3 | namespace details{ 4 | 5 | BasePool::BasePool(size_t element_size, size_t chunk_size) : 6 | size_(0), 7 | capacity_(0), 8 | element_size_(element_size), 9 | chunk_size_(chunk_size) { 10 | } 11 | 12 | BasePool::~BasePool() { 13 | for (char *ptr : chunks_) { 14 | delete[] ptr; 15 | } 16 | } 17 | void BasePool::ensure_min_size(std::size_t size) { 18 | if (size >= size_) { 19 | if (size >= capacity_) ensure_min_capacity(size); 20 | size_ = size; 21 | } 22 | } 23 | void BasePool::ensure_min_capacity(size_t min_capacity) { 24 | while (min_capacity >= capacity_) { 25 | char *chunk = new char[element_size_ * chunk_size_]; 26 | chunks_.push_back(chunk); 27 | capacity_ += chunk_size_; 28 | } 29 | } 30 | 31 | template 32 | Pool::Pool(size_t chunk_size) : BasePool(sizeof(T), chunk_size) { } 33 | 34 | template 35 | void Pool::destroy(index_t index) { 36 | ECS_ASSERT(index < size_, "Pool has not allocated memory for this index."); 37 | T *ptr = get_ptr(index); 38 | ptr->~T(); 39 | } 40 | 41 | template 42 | inline T* Pool::get_ptr(index_t index) { 43 | ECS_ASSERT(index < capacity_, "Pool has not allocated memory for this index."); 44 | return reinterpret_cast(chunks_[index / chunk_size_] + (index % chunk_size_) * element_size_); 45 | } 46 | 47 | template 48 | inline const T* Pool::get_ptr(index_t index) const { 49 | ECS_ASSERT(index < this->capacity_, "Pool has not allocated memory for this index."); 50 | return reinterpret_cast(chunks_[index / chunk_size_] + (index % chunk_size_) * element_size_); 51 | } 52 | 53 | template 54 | inline T & Pool::get(index_t index) { 55 | return *get_ptr(index); 56 | } 57 | 58 | template 59 | inline const T & Pool::get(index_t index) const { 60 | return *get_ptr(index); 61 | } 62 | 63 | template 64 | inline T & Pool::operator[](size_t index) { 65 | return get(index); 66 | } 67 | 68 | template 69 | inline const T & Pool::operator[](size_t index) const { 70 | return get(index); 71 | } 72 | 73 | } // namespace details 74 | 75 | } // namespace ecs -------------------------------------------------------------------------------- /include/Property.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_PROPERTY_H 2 | #define ECS_PROPERTY_H 3 | 4 | namespace ecs{ 5 | 6 | namespace details{ 7 | ///--------------------------------------------------------------------- 8 | /// Helper class used for compile-time checks to determine if a 9 | /// component is a propery. 10 | ///--------------------------------------------------------------------- 11 | class BaseProperty{}; 12 | 13 | } // namespace details 14 | 15 | ///--------------------------------------------------------------------- 16 | /// A Property is a helper class for Component with only one 17 | /// property of any type 18 | ///--------------------------------------------------------------------- 19 | /// 20 | /// A Property is a helper class for Component with only one 21 | /// property of any type. 22 | /// 23 | /// it implements standard constructors, type conversations and 24 | /// operators: 25 | /// 26 | /// ==, != 27 | /// >=, <=, <, > 28 | /// +=, -=, *=, /=, %= 29 | /// &=, |=, ^= 30 | /// +, -, *; /, % 31 | /// &, |, ^, ~ 32 | /// >>, << 33 | /// ++, -- 34 | /// 35 | /// TODO: Add more operators? 36 | ///--------------------------------------------------------------------- 37 | template 38 | struct Property : details::BaseProperty{ 39 | Property() { } 40 | Property(const T &value) : value(value) { } 41 | 42 | operator const T &() const { return value; } 43 | operator T &() { return value; } 44 | 45 | T value; 46 | using ValueType = T; 47 | }; 48 | 49 | /// Comparision operators 50 | template bool operator==(Property const &lhs, const E &rhs); 51 | template bool operator!=(Property const &lhs, const E &rhs); 52 | template bool operator>=(Property const &lhs, const E &rhs); 53 | template bool operator> (Property const &lhs, const E &rhs); 54 | template bool operator<=(Property const &lhs, const E &rhs); 55 | template bool operator< (Property const &lhs, const E &rhs); 56 | 57 | /// Compound assignment operators 58 | template T& operator+=(Property &lhs, const E &rhs); 59 | template T& operator-=(Property &lhs, const E &rhs); 60 | template T& operator*=(Property &lhs, const E &rhs); 61 | template T& operator/=(Property &lhs, const E &rhs); 62 | template T& operator%=(Property &lhs, const E &rhs); 63 | template T& operator&=(Property &lhs, const E &rhs); 64 | template T& operator|=(Property &lhs, const E &rhs); 65 | template T& operator^=(Property &lhs, const E &rhs); 66 | 67 | /// Arithmetic operators 68 | template T operator+ (Property &lhs, const E &rhs); 69 | template T operator- (Property &lhs, const E &rhs); 70 | template T operator* (Property &lhs, const E &rhs); 71 | template T operator/ (Property &lhs, const E &rhs); 72 | template T operator% (Property &lhs, const E &rhs); 73 | template T& operator++(Property &lhs); 74 | template T operator++(Property &lhs, int); 75 | template T& operator--(Property &lhs); 76 | template T operator--(Property &lhs, int); 77 | 78 | /// Bitwise operators 79 | template T operator&(Property &lhs, const E &rhs); 80 | template T operator|(Property &lhs, const E &rhs); 81 | template T operator^(Property &lhs, const E &rhs); 82 | template T operator~(Property &lhs); 83 | template T operator>>(Property &lhs, const E &rhs); 84 | template T operator<<(Property &lhs, const E &rhs); 85 | 86 | } // namespace ecs 87 | 88 | #include "Property.inl" 89 | 90 | #endif //ECS_PROPERTY_H 91 | -------------------------------------------------------------------------------- /include/Property.inl: -------------------------------------------------------------------------------- 1 | namespace ecs{ 2 | 3 | 4 | template 5 | inline bool operator==(Property const &lhs, const E &rhs) { 6 | return lhs.value == rhs; 7 | } 8 | 9 | template 10 | inline bool operator!=(Property const &lhs, const E &rhs) { 11 | return lhs.value != rhs; 12 | } 13 | 14 | template 15 | inline bool operator>=(Property const &lhs, const E &rhs) { 16 | return lhs.value >= rhs; 17 | } 18 | 19 | template 20 | inline bool operator>(Property const &lhs, const E &rhs) { 21 | return lhs.value > rhs; 22 | } 23 | 24 | template 25 | inline bool operator<=(Property const &lhs, const E &rhs) { 26 | return lhs.value <= rhs; 27 | } 28 | 29 | template 30 | inline bool operator<(Property const &lhs, const E &rhs) { 31 | return lhs.value < rhs; 32 | } 33 | 34 | template 35 | inline T &operator+=(Property &lhs, const E &rhs) { 36 | return lhs.value += rhs; 37 | } 38 | 39 | template 40 | inline T &operator-=(Property &lhs, const E &rhs) { 41 | return lhs.value -= rhs; 42 | } 43 | 44 | template 45 | inline T &operator*=(Property &lhs, const E &rhs) { 46 | return lhs.value *= rhs; 47 | } 48 | 49 | template 50 | inline T &operator/=(Property &lhs, const E &rhs) { 51 | return lhs.value /= rhs; 52 | } 53 | 54 | template 55 | inline T &operator%=(Property &lhs, const E &rhs) { 56 | return lhs.value %= rhs; 57 | } 58 | 59 | template 60 | inline T &operator&=(Property &lhs, const E &rhs) { 61 | return lhs.value &= rhs; 62 | } 63 | 64 | template 65 | inline T &operator|=(Property &lhs, const E &rhs) { 66 | return lhs.value |= rhs; 67 | } 68 | 69 | template 70 | inline T &operator^=(Property &lhs, const E &rhs) { 71 | return lhs.value ^= rhs; 72 | } 73 | 74 | template 75 | inline T operator+(Property &lhs, const E &rhs) { 76 | return lhs.value + rhs; 77 | } 78 | 79 | template 80 | inline T operator-(Property &lhs, const E &rhs) { 81 | return lhs.value - rhs; 82 | } 83 | 84 | template 85 | inline T operator*(Property &lhs, const E &rhs) { 86 | return lhs.value * rhs; 87 | } 88 | 89 | template 90 | inline T operator/(Property &lhs, const E &rhs) { 91 | return lhs.value / rhs; 92 | } 93 | 94 | template 95 | inline T operator%(Property &lhs, const E &rhs) { 96 | return lhs.value % rhs; 97 | } 98 | 99 | template 100 | inline T &operator++(Property &lhs) { 101 | ++lhs.value; 102 | return lhs.value; 103 | } 104 | 105 | template 106 | inline T operator++(Property &lhs, int) { 107 | T copy = lhs; 108 | ++lhs; 109 | return copy; 110 | } 111 | 112 | template 113 | inline T& operator--(Property &lhs) { 114 | --lhs.value; 115 | return lhs.value; 116 | } 117 | 118 | template 119 | inline T operator--(Property &lhs, int) { 120 | T copy = lhs; 121 | --lhs; 122 | return copy; 123 | } 124 | 125 | template 126 | inline T operator&(Property &lhs, const E &rhs) { 127 | return lhs.value & rhs; 128 | } 129 | 130 | template 131 | inline T operator|(Property &lhs, const E &rhs) { 132 | return lhs.value | rhs; 133 | } 134 | 135 | template 136 | inline T operator^(Property &lhs, const E &rhs) { 137 | return lhs.value ^ rhs; 138 | } 139 | 140 | template 141 | inline T operator~(Property &lhs) { 142 | return ~lhs.value; 143 | } 144 | 145 | template 146 | inline T operator>>(Property &lhs, const E &rhs) { 147 | return lhs.value >> rhs; 148 | } 149 | 150 | template 151 | inline T operator<<(Property &lhs, const E &rhs) { 152 | return lhs.value << rhs; 153 | } 154 | 155 | } // namespace ecs 156 | 157 | ///--------------------------------------------------------------------- 158 | /// This will allow a property to be streamed into a input and output 159 | /// stream. 160 | ///--------------------------------------------------------------------- 161 | namespace std { 162 | 163 | template 164 | ostream &operator<<(ostream &os, const ecs::Property &obj) { 165 | return os << obj.value; 166 | } 167 | 168 | template 169 | istream &operator>>(istream &is, ecs::Property &obj) { 170 | return is >> obj.value; 171 | } 172 | 173 | } //namespace std 174 | 175 | ///--------------------------------------------------------------------- 176 | /// This will enable properties to be added to a string 177 | ///--------------------------------------------------------------------- 178 | template 179 | std::string operator+(const std::string &lhs, const ecs::Property &rhs) { return lhs + rhs.value; } 180 | 181 | template 182 | std::string operator+(std::string &&lhs, ecs::Property &&rhs) { return lhs + rhs.value; } 183 | 184 | template 185 | std::string operator+(std::string &&lhs, const ecs::Property &rhs) { return lhs + rhs.value; } 186 | 187 | template 188 | std::string operator+(const std::string &lhs, ecs::Property &&rhs) { return lhs + rhs.value; } -------------------------------------------------------------------------------- /include/System.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Robin Grönberg on 29/11/15. 3 | // 4 | 5 | #ifndef OPENECS_SYSTEM_H 6 | #define OPENECS_SYSTEM_H 7 | 8 | namespace ecs { 9 | 10 | class EntityManager; 11 | class SystemManager; 12 | 13 | ///--------------------------------------------------------------------- 14 | /// A system is responsible for some kind of behavior for entities 15 | /// with certain components 16 | ///--------------------------------------------------------------------- 17 | /// 18 | /// A system implements behavior of entities with required components 19 | /// The update method is called every frame/update from the 20 | /// SystemManager. 21 | /// 22 | ///--------------------------------------------------------------------- 23 | class System { 24 | public: 25 | virtual ~System() { } 26 | virtual void update(float time) = 0; 27 | protected: 28 | inline EntityManager &entities(); 29 | private: 30 | friend class SystemManager; 31 | SystemManager *manager_; 32 | }; 33 | 34 | } //namespace ecs 35 | 36 | #include "System.inl" 37 | 38 | #endif //OPENECS_SYSTEM_H 39 | -------------------------------------------------------------------------------- /include/System.inl: -------------------------------------------------------------------------------- 1 | #include "SystemManager.h" 2 | 3 | namespace ecs{ 4 | 5 | EntityManager& System::entities(){ 6 | return *manager_->entities_; 7 | } 8 | 9 | } //namespace ecs 10 | -------------------------------------------------------------------------------- /include/SystemManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Robin Grönberg on 29/11/15. 3 | // 4 | 5 | #ifndef OPENECS_SYSTEM_MANAGER_H 6 | #define OPENECS_SYSTEM_MANAGER_H 7 | 8 | namespace ecs { 9 | 10 | class EntityManager; 11 | class System; 12 | 13 | ///--------------------------------------------------------------------- 14 | /// A SystemManager is responsible for managing Systems. 15 | ///--------------------------------------------------------------------- 16 | /// 17 | /// A SystemManager is associated with an EntityManager and any 18 | /// number of systems. Calling update calls update on every system. 19 | /// Each system can access the EntityManager and perform operations 20 | /// on the entities. 21 | /// 22 | ///--------------------------------------------------------------------- 23 | class SystemManager: details::forbid_copies { 24 | public: 25 | 26 | inline SystemManager(EntityManager &entities) : entities_(&entities) { } 27 | 28 | inline ~SystemManager(); 29 | 30 | /// Adds a System to this SystemManager. 31 | template 32 | inline S &add(Args &&... args); 33 | 34 | /// Removes a System from this System Manager 35 | template 36 | inline void remove(); 37 | 38 | /// Update all attached systems. They are updated in the order they are added 39 | inline void update(float time); 40 | 41 | /// Check if a system is attached. 42 | template 43 | inline bool exists(); 44 | 45 | private: 46 | std::vector systems_; 47 | std::vector order_; 48 | EntityManager *entities_; 49 | 50 | friend class System; 51 | }; 52 | 53 | } // namespace ecs 54 | 55 | #include "SystemManager.inl" 56 | 57 | #endif //OPENECS_SYSTEM_MANAGER_H 58 | -------------------------------------------------------------------------------- /include/SystemManager.inl: -------------------------------------------------------------------------------- 1 | #include "System.h" 2 | 3 | namespace ecs{ 4 | 5 | SystemManager::~SystemManager() { 6 | for (auto system : systems_) { 7 | if (system != nullptr) delete system; 8 | } 9 | systems_.clear(); 10 | order_.clear(); 11 | } 12 | 13 | template 14 | S& SystemManager::add(Args &&... args) { 15 | ECS_ASSERT_IS_SYSTEM(S); 16 | ECS_ASSERT(!exists(), "System already exists"); 17 | systems_.resize(details::system_index() + 1); 18 | S *system = new S(std::forward(args) ...); 19 | system->manager_ = this; 20 | systems_[details::system_index()] = system; 21 | order_.push_back(details::system_index()); 22 | return *system; 23 | } 24 | 25 | template 26 | void SystemManager::remove() { 27 | ECS_ASSERT_IS_SYSTEM(S); 28 | ECS_ASSERT(exists(), "System does not exist"); 29 | delete systems_[details::system_index()]; 30 | systems_[details::system_index()] = nullptr; 31 | for (auto it = order_.begin(); it != order_.end(); ++it) { 32 | if (*it == details::system_index()) { 33 | order_.erase(it); 34 | break; 35 | } 36 | } 37 | } 38 | 39 | void SystemManager::update(float time) { 40 | for (auto index : order_) { 41 | systems_[index]->update(time); 42 | } 43 | } 44 | 45 | template 46 | inline bool SystemManager::exists() { 47 | ECS_ASSERT_IS_SYSTEM(S); 48 | return systems_.size() > details::system_index() && systems_[details::system_index()] != nullptr; 49 | } 50 | 51 | } // namespace ecs 52 | -------------------------------------------------------------------------------- /include/UnallocatedEntity.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Robin Grönberg on 05/12/15. 3 | // 4 | 5 | #ifndef OPENECS_UNALLOCATEDENTITY_H 6 | #define OPENECS_UNALLOCATEDENTITY_H 7 | 8 | #include 9 | #include "Entity.h" 10 | #include "Defines.h" 11 | 12 | namespace ecs{ 13 | 14 | ///--------------------------------------------------------------------- 15 | /// UnallocatedEntity is used when creating an Entity but postponing 16 | /// allocation for memory in the EntityManager 17 | ///--------------------------------------------------------------------- 18 | /// 19 | /// UnallocatedEntity is used when creating an Entity from the 20 | /// EntityManager but does not allocate memory inside the EntityManager 21 | /// until the UnallocatedEntity is assigned, or goes out of scope. 22 | /// 23 | /// The purpose of doing this is to postpone the placement of the Entity 24 | /// until components have been attached. Then, the EntityManager knows 25 | /// what components that the entity has, and can place the entity close 26 | /// to similar entities in memory. This leads to better usage of the 27 | /// CPU cache 28 | /// 29 | /// An UnallocatedEntity can always be implicitly casted to an Entity. 30 | /// 31 | /// Au UnallocatedEntity is assumed unallocated as long as entity_ is 32 | /// a nullptr 33 | ///--------------------------------------------------------------------- 34 | class UnallocatedEntity { 35 | private: 36 | struct ComponentHeader{ 37 | unsigned int index, size; 38 | }; 39 | public: 40 | inline UnallocatedEntity(EntityManager &manager); 41 | inline UnallocatedEntity &operator=(const UnallocatedEntity &rhs); 42 | inline ~UnallocatedEntity(); 43 | 44 | /// Cast to Entity or EntityAlias 45 | inline operator Entity &(); 46 | 47 | inline bool operator==(const UnallocatedEntity &rhs) const; 48 | inline bool operator!=(const UnallocatedEntity &rhs) const; 49 | inline bool operator==(const Entity &rhs) const; 50 | inline bool operator!=(const Entity &rhs) const; 51 | 52 | inline Id &id(); 53 | 54 | /// Returns the requested component, or error if it doesn't exist 55 | template inline C &get(); 56 | template inline C const &get() const; 57 | 58 | /// Set the requested component, if old component exist, 59 | /// a new one is created. Otherwise, the assignment operator 60 | /// is used. 61 | template inline C &set(Args &&... args); 62 | 63 | /// Add the requested component, error if component of the same type exist already 64 | template inline C &add(Args &&... args); 65 | 66 | /// Access this Entity as an EntityAlias. 67 | template inline T &as(); 68 | 69 | /// Assume that this entity has provided Components 70 | /// Use for faster component access calls 71 | template inline EntityAlias &assume(); 72 | 73 | /// Removes a component. Error of it doesn't exist 74 | template inline void remove(); 75 | 76 | /// Removes all components and call destructors 77 | void inline remove_everything(); 78 | 79 | /// Clears the component mask without destroying components (faster than remove_everything) 80 | void inline clear_mask(); 81 | 82 | /// Destroys this entity. Removes all components as well 83 | void inline destroy(); 84 | 85 | /// Return true if entity has all specified components. False otherwise 86 | template inline bool has() const; 87 | 88 | /// Returns whether an entity is an entity alias or not 89 | template inline bool is() const; 90 | 91 | /// Returns true if entity has not been destroyed. False otherwise 92 | bool inline is_valid() const; 93 | 94 | /// Allocates memory for this Entity. Once allocated, the Unallocated Entity function like a 95 | /// normal Entity 96 | void inline allocate(); 97 | 98 | private: 99 | 100 | bool inline is_allocated() const; 101 | 102 | EntityManager * manager_ = nullptr; 103 | Entity entity_; 104 | std::vector component_data; 105 | std::vector component_headers_; 106 | details::ComponentMask mask_ = details::ComponentMask(0); 107 | 108 | }; //UnallocatedEntity 109 | 110 | } // namespace ecs 111 | 112 | 113 | #include "UnallocatedEntity.inl" 114 | 115 | #endif //OPENECS_UNALLOCATEDENTITY_H 116 | -------------------------------------------------------------------------------- /include/UnallocatedEntity.inl: -------------------------------------------------------------------------------- 1 | #include "UnallocatedEntity.h" 2 | #include "ComponentManager.h" 3 | #include 4 | 5 | namespace ecs{ 6 | 7 | UnallocatedEntity::UnallocatedEntity(EntityManager& manager) : manager_(&manager), entity_(&manager, Id(index_t(-1),0)){ } 8 | 9 | UnallocatedEntity::~UnallocatedEntity(){ 10 | allocate(); 11 | } 12 | 13 | UnallocatedEntity& UnallocatedEntity::operator=(const UnallocatedEntity &rhs){ 14 | allocate(); 15 | manager_ = rhs.manager_; 16 | entity_ = rhs.entity_; 17 | component_data = rhs.component_data; 18 | component_headers_ = rhs.component_headers_; 19 | return *this; 20 | } 21 | 22 | UnallocatedEntity::operator Entity &() { 23 | allocate(); 24 | return entity_; 25 | } 26 | 27 | Id& UnallocatedEntity::id() { 28 | allocate(); 29 | return entity_.id(); 30 | } 31 | 32 | template 33 | C& UnallocatedEntity::get() { 34 | ECS_ASSERT(is_valid(), "Unallocated Entity invalid"); 35 | ECS_ASSERT(has(), "UnallocatedEntity does not have Component Attached"); 36 | if(is_allocated()){ 37 | return entity_.get(); 38 | } 39 | int index = 0; 40 | for (auto& componentHeader : component_headers_) { 41 | if(componentHeader.index == details::component_index()){ 42 | return *reinterpret_cast(&component_data[index]); 43 | } 44 | index += componentHeader.size; 45 | } 46 | //should not happen 47 | return *static_cast(nullptr); 48 | } 49 | 50 | template 51 | C const & UnallocatedEntity::get() const { 52 | ECS_ASSERT(is_valid(), "Unallocated Entity invalid"); 53 | ECS_ASSERT(has(), "UnallocatedEntity does not have Component Attached"); 54 | if(is_allocated()){ 55 | return entity_.get(); 56 | }else{ 57 | int index = 0; 58 | for (auto& componentHeader : component_headers_) { 59 | if(componentHeader.index == details::component_index()){ 60 | return *reinterpret_cast(&component_data[index]); 61 | } 62 | index += componentHeader.size; 63 | } 64 | } 65 | //should not happen 66 | return *static_cast(nullptr); 67 | } 68 | 69 | template 70 | C& UnallocatedEntity::set(Args &&... args){ 71 | if(is_allocated()){ 72 | return entity_.set(std::forward(args)...); 73 | } 74 | if(has()){ 75 | return get() = manager_->create_tmp_component(std::forward(args)...); 76 | }else{ 77 | return add(std::forward(args)...); 78 | } 79 | } 80 | 81 | template 82 | C& UnallocatedEntity::add(Args &&... args){ 83 | if(is_allocated()){ 84 | return entity_.add(std::forward(args)...); 85 | } 86 | ECS_ASSERT(!has(), "Unallocated Entity cannot assign already assigned component with add. Use set instead"); 87 | ECS_ASSERT(is_valid(), "Unallocated Entity invalid"); 88 | int index = 0; 89 | //Find component location in memory 90 | for (auto componentHeader : component_headers_) { 91 | index += componentHeader.size; 92 | } 93 | //Ensure that a component manager exists for C 94 | manager_->get_component_manager(); 95 | //Set component data 96 | auto component_index = details::component_index(); 97 | mask_.set(component_index); 98 | component_headers_.push_back(ComponentHeader{static_cast(component_index), sizeof(C)}); 99 | component_data.resize(index + sizeof(C)); 100 | return details::create_component(&component_data[index], std::forward(args)...); 101 | } 102 | 103 | template 104 | C & UnallocatedEntity::as() { 105 | allocate(); 106 | return entity_.as(); 107 | } 108 | 109 | template 110 | EntityAlias & UnallocatedEntity::assume() { 111 | allocate(); 112 | return entity_.assume(); 113 | } 114 | 115 | template 116 | void UnallocatedEntity::remove() { 117 | if(is_allocated()){ 118 | entity_.remove(); 119 | }else{ 120 | int index = 0; 121 | auto component_index = details::component_index(); 122 | for (auto& componentHeader : component_headers_) { 123 | if(componentHeader.index == component_index){ 124 | C& component = *reinterpret_cast(&component_data[index]); 125 | component.~C(); 126 | break; 127 | } 128 | index += componentHeader.size; 129 | } 130 | mask_.reset(component_index); 131 | } 132 | } 133 | 134 | void UnallocatedEntity::remove_everything() { 135 | if(is_allocated()){ 136 | entity_.remove_everything(); 137 | }else{ 138 | //TODO: call dtors on components 139 | component_headers_.clear(); 140 | mask_.reset(); 141 | } 142 | } 143 | 144 | void UnallocatedEntity::clear_mask() { 145 | if(is_allocated()){ 146 | entity_.clear_mask(); 147 | } else{ 148 | component_headers_.clear(); 149 | mask_.reset(); 150 | } 151 | } 152 | 153 | void UnallocatedEntity::destroy() { 154 | if(is_allocated()){ 155 | entity_.destroy(); 156 | }else{ 157 | manager_ = nullptr; 158 | } 159 | } 160 | 161 | template 162 | bool UnallocatedEntity::has() const { 163 | if(is_allocated()){ 164 | return entity_.has(); 165 | }else{ 166 | auto component_mask = details::component_mask(); 167 | return (component_mask & mask_) == component_mask; 168 | } 169 | } 170 | 171 | template 172 | bool UnallocatedEntity::is() const { 173 | if(is_allocated()){ 174 | return entity_.is(); 175 | }else{ 176 | ECS_ASSERT_IS_ENTITY(T); 177 | ECS_ASSERT_ENTITY_CORRECT_SIZE(T); 178 | auto component_mask = T::static_mask(); 179 | return (component_mask & mask_) == component_mask; 180 | } 181 | } 182 | 183 | bool UnallocatedEntity::is_valid() const { 184 | if(is_allocated()){ 185 | return entity_.is_valid(); 186 | }else{ 187 | return manager_ != nullptr; 188 | } 189 | } 190 | 191 | inline bool operator==(const UnallocatedEntity &lhs, const UnallocatedEntity &rhs) { 192 | return &lhs == &rhs; 193 | } 194 | 195 | inline bool operator!=(const UnallocatedEntity &lhs, const UnallocatedEntity &rhs) { 196 | return &lhs != &rhs; 197 | } 198 | 199 | inline bool UnallocatedEntity::operator==(const Entity &rhs) const { 200 | return &entity_ == &rhs; 201 | } 202 | 203 | inline bool UnallocatedEntity::operator!=(const Entity &rhs) const { 204 | return &entity_ != &rhs; 205 | } 206 | 207 | bool UnallocatedEntity::is_allocated() const { 208 | return manager_ == nullptr; 209 | } 210 | 211 | void UnallocatedEntity::allocate() { 212 | if(!is_allocated()){ 213 | entity_ = manager_->create_with_mask(mask_); 214 | if(component_headers_.size() > 0){ 215 | auto index = entity_.id().index(); 216 | manager_->mask(index) |= mask_; 217 | unsigned int offset = 0; 218 | //TODO: set mask 219 | for (auto componentHeader : component_headers_) { 220 | details::BaseManager& componentManager = manager_->get_component_manager(componentHeader.index); 221 | componentManager.ensure_min_size(index); 222 | //Copy data from tmp location to acctuial location in component manager 223 | std::memcpy(componentManager.get_void_ptr(index), &component_data[offset], componentHeader.size); 224 | offset+=componentHeader.size; 225 | } 226 | } 227 | manager_ = nullptr; 228 | } 229 | } 230 | 231 | } // namespace ecs -------------------------------------------------------------------------------- /include/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_UTILS_H 2 | #define ECS_UTILS_H 3 | 4 | ///-------------------------------------------------------------------- 5 | /// This file contains useful classes for meta-programming or 6 | /// nice helper functions 7 | ///-------------------------------------------------------------------- 8 | 9 | namespace ecs{ 10 | 11 | /// Forward declarations 12 | class Entity; 13 | 14 | namespace details{ 15 | 16 | ///-------------------------------------------------------------------- 17 | /// function_traits is used to determine function properties 18 | /// such as parameter types (arguments) and return type. 19 | ///-------------------------------------------------------------------- 20 | template 21 | struct function_traits 22 | : public function_traits { 23 | }; 24 | // For generic types, directly use the result of the signature of its 'operator()' 25 | 26 | template 27 | struct function_traits 28 | // we specialize for pointers to member function 29 | { 30 | enum { arg_count = sizeof...(Args) }; 31 | 32 | typedef ReturnType return_type; 33 | 34 | template 35 | struct arg_t { 36 | typedef typename std::tuple_element>::type type; 37 | }; 38 | template 39 | using arg = typename arg_t::type; 40 | 41 | template 42 | struct arg_remove_ref_t { 43 | typedef typename std::remove_reference>::type type; 44 | }; 45 | template 46 | using arg_remove_ref = typename arg_remove_ref_t::type; 47 | 48 | typedef std::tuple args; 49 | }; 50 | 51 | 52 | ///--------------------------------------------------------------------- 53 | /// Determine if type is part of a list of types 54 | ///--------------------------------------------------------------------- 55 | template 56 | struct is_type; 57 | 58 | template 59 | struct is_type: std::true_type { }; 60 | 61 | template 62 | struct is_type: std::true_type { }; 63 | 64 | template 65 | struct is_type: std::false_type { }; 66 | 67 | template 68 | struct is_type: is_type::type { }; 69 | 70 | 71 | ///--------------------------------------------------------------------- 72 | /// Check if a class has implemented operator() 73 | /// 74 | /// Mostly used for checking if provided type is a lambda expression 75 | ///--------------------------------------------------------------------- 76 | /// 77 | template 78 | struct is_callable { 79 | template 80 | static char test(decltype(&U::operator())); 81 | 82 | template 83 | static int test(...); 84 | 85 | enum { value = sizeof(test(0)) == sizeof(char) }; 86 | }; 87 | 88 | 89 | ///--------------------------------------------------------------------- 90 | /// Any class that should not be able to copy itself inherit this class 91 | ///--------------------------------------------------------------------- 92 | /// 93 | class forbid_copies { 94 | protected: 95 | forbid_copies() = default; 96 | ~forbid_copies() = default; 97 | forbid_copies(const forbid_copies &) = delete; 98 | forbid_copies &operator=(const forbid_copies &) = delete; 99 | }; 100 | 101 | ///-------------------------------------------------------------------- 102 | /// Helper functions 103 | ///-------------------------------------------------------------------- 104 | 105 | inline size_t &component_counter() { 106 | static size_t counter = 0; 107 | return counter; 108 | } 109 | 110 | inline size_t inc_component_counter() { 111 | size_t index = component_counter()++; 112 | ECS_ASSERT(index < ECS_MAX_NUM_OF_COMPONENTS, "maximum number of components exceeded."); 113 | return index; 114 | } 115 | 116 | template 117 | size_t component_index() { 118 | static size_t index = inc_component_counter(); 119 | return index; 120 | } 121 | 122 | inline size_t &system_counter() { 123 | static size_t counter = 0; 124 | return counter; 125 | } 126 | 127 | template 128 | size_t system_index() { 129 | static size_t index = system_counter()++; 130 | return index; 131 | } 132 | 133 | //C1 should not be Entity 134 | template 135 | inline auto component_mask() -> typename 136 | std::enable_if::value, ComponentMask>::type { 137 | ComponentMask mask = ComponentMask((1UL << component_index())); 138 | return mask; 139 | } 140 | 141 | //When C1 is Entity, ignore 142 | template 143 | inline auto component_mask() -> typename 144 | std::enable_if::value, ComponentMask>::type { 145 | return ComponentMask(0); 146 | } 147 | 148 | //recursive function for component_mask creation 149 | template 150 | inline auto component_mask() -> typename 151 | std::enable_if::value, ComponentMask>::type { 152 | ComponentMask mask = component_mask() | component_mask(); 153 | return mask; 154 | } 155 | 156 | } // namespace details 157 | 158 | } // namespace ecs 159 | 160 | #endif //ECS_UTILS_H 161 | -------------------------------------------------------------------------------- /include/View.h: -------------------------------------------------------------------------------- 1 | #ifndef ECS_VIEW_H 2 | #define ECS_VIEW_H 3 | 4 | namespace ecs{ 5 | 6 | ///--------------------------------------------------------------------- 7 | /// Helper class that is used to access the iterator 8 | ///--------------------------------------------------------------------- 9 | /// @usage Calling entities.with(), returns a view 10 | /// that can be used to access the iterator with begin() and 11 | /// end() that iterates through all entities with specified 12 | /// Components 13 | ///--------------------------------------------------------------------- 14 | template 15 | class View { 16 | ECS_ASSERT_IS_ENTITY(T) 17 | public: 18 | using iterator = Iterator; 19 | using const_iterator = Iterator; 20 | 21 | View(EntityManager *manager, details::ComponentMask mask); 22 | 23 | iterator begin(); 24 | iterator end(); 25 | const_iterator begin() const; 26 | const_iterator end() const; 27 | index_t count(); 28 | template 29 | View&& with(); 30 | 31 | private: 32 | EntityManager *manager_; 33 | details::ComponentMask mask_; 34 | 35 | friend class EntityManager; 36 | }; //View 37 | 38 | } // namespace ecs 39 | 40 | #include "View.inl" 41 | 42 | #endif //OPENECS_VIEW_H 43 | -------------------------------------------------------------------------------- /include/View.inl: -------------------------------------------------------------------------------- 1 | #include "EntityManager.h" 2 | 3 | namespace ecs{ 4 | 5 | template 6 | View::View(EntityManager *manager, details::ComponentMask mask) : 7 | manager_(manager), 8 | mask_(mask) { } 9 | 10 | 11 | template 12 | typename View::iterator View::begin() { 13 | return iterator(manager_, mask_, true); 14 | } 15 | 16 | template 17 | typename View::iterator View::end() { 18 | return iterator(manager_, mask_, false); 19 | } 20 | 21 | template 22 | typename View::const_iterator View::begin() const { 23 | return const_iterator(manager_, mask_, true); 24 | } 25 | 26 | template 27 | typename View::const_iterator View::end() const { 28 | return const_iterator(manager_, mask_, false); 29 | } 30 | 31 | template 32 | inline index_t View::count() { 33 | index_t count = 0; 34 | for (auto it = begin(); it != end(); ++it) { 35 | ++count; 36 | } 37 | return count; 38 | } 39 | 40 | template template 41 | View&& View::with() { 42 | mask_ |= details::component_mask(); 43 | return *this; 44 | } 45 | 46 | } // namespace ecs -------------------------------------------------------------------------------- /include/ecs.h: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------- 2 | /// Copyright (C) 2015 Robin Grönberg 3 | /// 4 | /// This program is free software: you can redistribute it and/or modify 5 | /// it under the terms of the GNU General Public License as published by 6 | /// the Free Software Foundation, either version 3 of the License, or 7 | /// (at your option) any later version. 8 | /// 9 | /// This program is distributed in the hope that it will be useful, 10 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | /// GNU General Public License for more details. 13 | /// 14 | /// You should have received a copy of the GNU General Public License 15 | /// along with this program. If not, see . 16 | 17 | /// This file includes all other parts of the library. This files is 18 | /// included when the single header is generated. 19 | #ifndef ECS_MAIN_INCLUDE 20 | #define ECS_MAIN_INCLUDE 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | #include "Defines.h" 32 | #include "Pool.h" 33 | #include "ComponentManager.h" 34 | #include "Property.h" 35 | #include "Id.h" 36 | #include "Entity.h" 37 | #include "EntityAlias.h" 38 | #include "UnallocatedEntity.h" 39 | #include "Iterator.h" 40 | #include "View.h" 41 | #include "EntityManager.h" 42 | #include "SystemManager.h" 43 | #include "System.h" 44 | 45 | #endif //ECS_MAIN_INCLUDE -------------------------------------------------------------------------------- /scripts/generate_header.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import os 4 | import sys 5 | import re 6 | import datetime 7 | import string 8 | 9 | includesParser = re.compile( r'\s*#include\s*"(.*)"' ) 10 | commentParser1 = re.compile( r'^\s*/\*') 11 | commentParser2 = re.compile( r'^ \*') 12 | blankParser = re.compile( r'^\s*$') 13 | seenHeaders = set([]) 14 | 15 | path = os.path.dirname(os.path.realpath( os.path.dirname(sys.argv[0]))) 16 | rootPath = os.path.join( path, 'include/' ) 17 | outputPath = os.path.join( path, 'single_include/ecs.h' ) 18 | 19 | includeImpl = True 20 | 21 | out = open( outputPath, 'w' ) 22 | 23 | def write( line ): 24 | out.write( line ) 25 | 26 | def parseFile( path, filename ): 27 | f = open( path + filename, 'r' ) 28 | blanks = 0 29 | for line in f: 30 | m = includesParser.match( line ) 31 | if m: 32 | header = m.group(1) 33 | headerPath, sep, headerFile = header.rpartition( "/" ) 34 | if not headerFile in seenHeaders: 35 | seenHeaders.add( headerFile ) 36 | write( "// #included from: {0}\n".format( header ) ) 37 | if os.path.exists( path + headerPath + sep + headerFile ): 38 | parseFile( path + headerPath + sep, headerFile ) 39 | else: 40 | parseFile( rootPath + headerPath + sep, headerFile ) 41 | else: 42 | #if (not guardParser.match( line ) or defineParser.match( line ) ) and not commentParser1.match( line )and not commentParser2.match( line ): 43 | if blankParser.match( line ): 44 | blanks = blanks + 1 45 | else: 46 | blanks = 0 47 | if blanks < 2: 48 | write( line.rstrip() + "\n" ) 49 | 50 | out.write( "///\n" ) 51 | out.write( "/// OpenEcs v{0}\n".format( "0.1.101" ) ) 52 | out.write( "/// Generated: {0}\n".format( datetime.datetime.now() ) ) 53 | out.write( "/// ----------------------------------------------------------\n" ) 54 | out.write( "/// This file has been generated from multiple files. Do not modify\n" ) 55 | out.write( "/// ----------------------------------------------------------\n" ) 56 | out.write( "///\n" ) 57 | out.write( "#ifndef ECS_SINGLE_INCLUDE_H\n" ) 58 | out.write( "#define ECS_SINGLE_INCLUDE_H\n" ) 59 | 60 | parseFile( rootPath, 'ecs.h' ) 61 | 62 | out.write( "#endif // ECS_SINGLE_INCLUDE_H\n\n" ) 63 | 64 | print ("Generated single include for OpenEcs\n" ) -------------------------------------------------------------------------------- /scripts/travis.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cmake -DCMAKE_BUILD_TYPE=Debug 3 | make UnitTests 4 | make PerformanceTests 5 | ./UnitTests -------------------------------------------------------------------------------- /test/common/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "thirdparty/catch.hpp" 3 | #include "ecs.h" -------------------------------------------------------------------------------- /test/ecs.cpp: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------- 2 | /// Copyright (C) 2015 Robin Grönberg 3 | /// 4 | /// This program is free software: you can redistribute it and/or modify 5 | /// it under the terms of the GNU General Public License as published by 6 | /// the Free Software Foundation, either version 3 of the License, or 7 | /// (at your option) any later version. 8 | /// 9 | /// This program is distributed in the hope that it will be useful, 10 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | /// GNU General Public License for more details. 13 | /// 14 | /// You should have received a copy of the GNU General Public License 15 | /// along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | #include "common/thirdparty/catch.hpp" 20 | 21 | #define ECS_ASSERT(Expr, Msg) if(!(Expr)) throw std::runtime_error(Msg); 22 | 23 | #include "ecs.h" 24 | 25 | using namespace ecs; 26 | 27 | namespace { 28 | 29 | int health_component_count; 30 | 31 | struct Health: Property { 32 | Health(short value) : Property(value) { 33 | health_component_count++; 34 | } 35 | 36 | inline Health &operator=(const short &value) { 37 | health_component_count++; 38 | this->value = value; 39 | return *this; 40 | } 41 | 42 | ~Health() { health_component_count--; } 43 | }; 44 | 45 | 46 | struct Mana: Property { 47 | // Needed for assignment operator 48 | Mana(float value) { 49 | this->value = value; 50 | } 51 | }; 52 | 53 | struct Weight: Property { }; 54 | 55 | struct Height: Property { }; 56 | 57 | struct Clothes { }; 58 | 59 | struct Shoes { }; 60 | 61 | struct Hat { }; 62 | 63 | struct Name: Property { 64 | //Name(const Name& name) = delete; 65 | }; 66 | 67 | struct Velocity { 68 | float x, y; 69 | }; 70 | 71 | struct Position { 72 | float x, y; 73 | }; 74 | 75 | struct Wheels { 76 | int number; 77 | }; 78 | 79 | struct Car: EntityAlias { 80 | 81 | Car(float x, float y) : Car() { 82 | drive(x, y); 83 | } 84 | 85 | Car() { 86 | add(); 87 | } 88 | 89 | void drive(float x, float y) { 90 | set(x, y); 91 | } 92 | 93 | bool is_moving() { 94 | if (!has()) return false; 95 | Velocity &vel = get(); 96 | return !(vel.x == 0 && vel.y == 0); 97 | } 98 | }; 99 | 100 | struct Character: EntityAlias { }; 101 | 102 | struct CountCarSystem: System { 103 | int count = 0; 104 | 105 | virtual void update(float time) { 106 | count = 0; 107 | entities().with([&](Wheels &wheels) { 108 | ++count; 109 | }); 110 | } 111 | }; 112 | 113 | struct RemoveDeadEntitiesSystem: System { 114 | virtual void update(float time) { 115 | for (auto entity : entities().with()) { 116 | if (entity.get() <= 0) { 117 | entity.destroy(); 118 | } 119 | } 120 | } 121 | }; 122 | 123 | } 124 | 125 | SCENARIO("Testing ecs framework, unittests") { 126 | GIVEN("An Entity Manager") { 127 | EntityManager entities; 128 | health_component_count = 0; 129 | 130 | //Testing entity functions 131 | GIVEN("1 Entitiy without components") { 132 | Entity entity = entities.create(); 133 | 134 | WHEN("Adding 4 Components to the entity") { 135 | entity.add(5); 136 | entity.add(10); 137 | entity.add(15); 138 | entity.add(20); 139 | THEN("They have the components attached") { 140 | bool has_health = entity.has(); 141 | bool has_health_and_mana = entity.has(); 142 | bool has_health_mana_and_weight = entity.has(); 143 | bool has_health_mana_weight_and_height = entity.has(); 144 | REQUIRE(has_health); 145 | REQUIRE(has_health_and_mana); 146 | REQUIRE(has_health_mana_and_weight); 147 | REQUIRE(has_health_mana_weight_and_height); 148 | } 149 | THEN("Accessing added components should work") { 150 | entity.get(); 151 | entity.get(); 152 | entity.get(); 153 | entity.get(); 154 | } 155 | THEN("Unpack data from added components should work") { 156 | REQUIRE(entity.get().value == 5); 157 | REQUIRE(entity.get().value == 10); 158 | REQUIRE(entity.get().value == 15); 159 | REQUIRE(entity.get().value == 20); 160 | } 161 | THEN("Accessing other components should not work") { 162 | REQUIRE_THROWS(entity.get()); 163 | REQUIRE_THROWS(entity.get()); 164 | REQUIRE_THROWS(entity.get()); 165 | } 166 | THEN("Only one Health component should exist") { 167 | REQUIRE(health_component_count == 1); 168 | } 169 | AND_WHEN("When removing the Health component") { 170 | entity.remove(); 171 | THEN("No Health component should exist") { 172 | REQUIRE(health_component_count == 0); 173 | } 174 | THEN("Entity should not have that component attached") { 175 | REQUIRE(!entity.has()); 176 | } 177 | } 178 | AND_WHEN("When removing the Health component twice") { 179 | entity.remove(); 180 | THEN("It should not work") { 181 | REQUIRE_THROWS(entity.remove()); 182 | } 183 | } 184 | AND_WHEN("When removing the Health component twice") { 185 | entity.remove(); 186 | THEN("It should not work") { 187 | REQUIRE_THROWS(entity.remove()); 188 | } 189 | } 190 | AND_WHEN("Destroying the entity") { 191 | entity.destroy(); 192 | THEN("No Health component should exist") { 193 | REQUIRE(health_component_count == 0); 194 | } 195 | } 196 | AND_WHEN("Accessing Component as ref and change value") { 197 | Health &health = entity.get(); 198 | health.value = 123; 199 | THEN("Health should be that value") { 200 | REQUIRE(entity.get().value == 123); 201 | } 202 | } 203 | AND_WHEN("Accessing Component as value and change value") { 204 | Health health = entity.get(); 205 | health.value = 123; 206 | THEN("Health should not be that value") { 207 | REQUIRE(entity.get().value != 123); 208 | } 209 | } 210 | } 211 | WHEN("Adding the same component twice with \"add\" method") { 212 | entity.add(1); 213 | THEN("Should not work") { 214 | REQUIRE_THROWS(entity.add(2)); 215 | } 216 | } 217 | WHEN("Adding the same component twice with \"set\" method") { 218 | entity.set(1); 219 | THEN("Should work") { 220 | entity.set(2); 221 | } 222 | } 223 | WHEN("Entity remains without components") { 224 | THEN("It should be valid") { 225 | REQUIRE(entity.is_valid()); 226 | } 227 | } 228 | WHEN("Adding some components to Entity") { 229 | THEN("It should still be valid") { 230 | REQUIRE(entity.is_valid()); 231 | } 232 | } 233 | WHEN("Destroying entity once") { 234 | entity.destroy(); 235 | THEN("It should be invalid") { 236 | REQUIRE(!entity.is_valid()); 237 | } 238 | } 239 | WHEN("Destroying entity twice") { 240 | entity.destroy(); 241 | THEN("It should not work") { 242 | REQUIRE_THROWS(entity.destroy()); 243 | } 244 | } 245 | WHEN("Destroying entity once and replace it with a new entity") { 246 | entity.destroy(); 247 | Entity entity2 = entities.create(); 248 | THEN("Old entity and new entity should have same id but old should be invalid") { 249 | REQUIRE(entity.id().index() == entity2.id().index()); 250 | REQUIRE(!entity.is_valid()); 251 | REQUIRE(entity2.is_valid()); 252 | } 253 | } 254 | WHEN("Adding Health and Mana with different values to entity") { 255 | entity.add(10); 256 | entity.add(20); 257 | THEN("Health and Mana should be different") { 258 | REQUIRE(entity.get() != entity.get()); 259 | REQUIRE(entity.get() != entity.get()); 260 | } 261 | } 262 | WHEN("Adding Health and Mana with same value to entity") { 263 | entity.add(10); 264 | entity.add(10); 265 | THEN("Health and Mana should be same") { 266 | REQUIRE(entity.get() == entity.get()); 267 | REQUIRE(entity.get() == entity.get()); 268 | } 269 | } 270 | } 271 | 272 | WHEN("Adding 100 entities") { 273 | auto new_entities = entities.create(100); 274 | THEN("Number of entities should be 100") { 275 | REQUIRE(entities.count() == 100); 276 | REQUIRE(entities.count() == new_entities.size()); 277 | } 278 | THEN("Iterating through the list should be the same") { 279 | for (size_t i = 0; i < new_entities.size(); ++i) { 280 | REQUIRE(new_entities[i] == entities[i]); 281 | } 282 | } 283 | WHEN("Removing all added entities") { 284 | for (size_t i = 0; i < new_entities.size(); ++i) { 285 | new_entities[i].destroy(); 286 | } 287 | THEN("EntityManager should be empty") { 288 | REQUIRE(entities.count() == 0); 289 | } 290 | } 291 | } 292 | 293 | WHEN("Adding 100 entities with health using lambda"){ 294 | entities.create(100, [] (Entity entity){ 295 | entity.add(10); 296 | }); 297 | THEN("There should be 100 entities with health"){ 298 | REQUIRE(entities.with().count() == 100); 299 | } 300 | } 301 | 302 | WHEN("Adding 100 cars using lambda"){ 303 | entities.create(100, [] (Car car){ 304 | car.add(); 305 | }); 306 | THEN("There should be 100 cars"){ 307 | REQUIRE(entities.fetch_every().count()); 308 | } 309 | } 310 | 311 | // Testing EntityManager::View functions 312 | GIVEN("4 entities with no components attached") { 313 | Entity e1 = entities.create(); 314 | Entity e2 = entities.create(); 315 | Entity e3 = entities.create(); 316 | Entity e4 = entities.create(); 317 | WHEN("Adding Health with same value to all but one entities") { 318 | e1.add(12); 319 | e2.add(12); 320 | e3.add(12); 321 | e4.add(100); 322 | THEN("All entities should have Health") { 323 | REQUIRE(entities.with().count() == entities.count()); 324 | } 325 | THEN("They should have same amout of getHealth (Not the one with different getHealth ofc)") { 326 | REQUIRE(e1.get() == e2.get()); 327 | } 328 | AND_WHEN("Adding Mana to 2 of them") { 329 | e1.add(0); 330 | e2.add(0); 331 | THEN("2 entities should have mana and getHealth") { 332 | size_t count = entities.with().count(); 333 | REQUIRE(count == 2); 334 | } 335 | THEN("Entities with Health and Mana == entities with Mana and Health (order independent)") { 336 | size_t count1 = entities.with().count(); 337 | size_t count2 = entities.with().count(); 338 | REQUIRE(count1 == count2); 339 | } 340 | } 341 | AND_WHEN("Accessing Health component as references") { 342 | short &health = e1.get(); 343 | THEN("Changing the value should affect the actual component") { 344 | health += 1; 345 | REQUIRE(health == (int) e1.get()); 346 | } 347 | } 348 | AND_WHEN("Accessing Health component as values") { 349 | int health = e1.get(); 350 | THEN("Changing the value should not affect the actual component") { 351 | health += 1; 352 | REQUIRE(health != (int) e1.get()); 353 | } 354 | } 355 | AND_WHEN("Accessing e1s' Health as pointer, and setting this pointer to e4s' different Health") { 356 | Health &health = e1.get(); 357 | health = e4.get(); 358 | THEN("Health of e1 should change") { 359 | REQUIRE(e4.get().value == e1.get().value); 360 | } 361 | } 362 | THEN("Iterating over entities with health as type Entity should compile") { 363 | for (Entity e : entities.with()) { (void) e; } 364 | } 365 | THEN("Iterating over entities with health as type EntityAlias should compile") { 366 | for (EntityAlias e : entities.with()) { (void) e; } 367 | } 368 | THEN("Iterating over entities with health as type EntityAlias should compile") { 369 | for (auto e : entities.with()) { 370 | typedef std::is_same> e_is_entity_alias; 371 | REQUIRE(e_is_entity_alias::value); 372 | } 373 | } 374 | } 375 | WHEN("Adding Mana to 2 of them") { 376 | e1.add(0); 377 | e2.add(0); 378 | THEN("2 entities should have mana") { 379 | REQUIRE(entities.with().count() == 2); 380 | } 381 | } 382 | } 383 | // Testing component operators 384 | GIVEN("An Entity with 2 Health and 10 Mana") { 385 | Entity entity = entities.create(); 386 | entity.add(2); 387 | entity.add(10); 388 | WHEN("Adding 2 Health with +=") { 389 | entity.get() += 2; 390 | THEN("Health should be 4") { 391 | REQUIRE(entity.get() == 4); 392 | } 393 | THEN("Health should be > 1") { 394 | REQUIRE(entity.get() > 1); 395 | } 396 | } 397 | WHEN("Multiplying Health with 2 using *=") { 398 | entity.get() *= 2; 399 | THEN("Health should be 4") { 400 | REQUIRE(entity.get() == 4); 401 | } 402 | } 403 | WHEN("Multiplying Health with 2 using [health = health * 2]") { 404 | entity.get() = entity.get() * 2; 405 | THEN("Health should be 4") { 406 | REQUIRE(entity.get() == 4); 407 | } 408 | } 409 | WHEN("Multiplying Health with 2 to an int variable") { 410 | int health = entity.get() * 2; 411 | THEN("Health should still be 2 and variable should be 4") { 412 | REQUIRE(entity.get() == 2); 413 | REQUIRE(health == 4); 414 | } 415 | } 416 | WHEN("Adding 2 Health with [health = health + 2]") { 417 | entity.get() = entity.get() + 2; 418 | THEN("Health should be 4") { 419 | REQUIRE(entity.get() == 4); 420 | } 421 | } 422 | WHEN("Adding 2 Health to an int variable") { 423 | int health = entity.get() + 2; 424 | THEN("Health should still be 0 and variable should be 2") { 425 | REQUIRE(entity.get() == 2); 426 | REQUIRE(health == 4); 427 | } 428 | } 429 | WHEN("Multiplying Health with 2 using *=") { 430 | entity.get() *= 2; 431 | THEN("Health should be 4") { 432 | REQUIRE(entity.get() == 4); 433 | } 434 | } 435 | WHEN("Subtracting 2 Health with -=") { 436 | entity.get() -= 2; 437 | THEN("Health should be 0") { 438 | REQUIRE(entity.get() == 0); 439 | } 440 | THEN("Health should be < -1") { 441 | REQUIRE(entity.get() < 1); 442 | } 443 | } 444 | WHEN("Dividing Health with 2 using /=") { 445 | entity.get() /= 2; 446 | THEN("Health should be 1") { 447 | REQUIRE(entity.get() == 1); 448 | } 449 | } 450 | WHEN("Dividing Health with 2 using [health = health * 2]") { 451 | entity.get() = entity.get() / 2; 452 | THEN("Health should be 1") { 453 | REQUIRE(entity.get() == 1); 454 | } 455 | } 456 | WHEN("Dividing Health with 2 to an int variable") { 457 | int health = entity.get() / 2; 458 | THEN("Health should still be 2 and variable should be 1") { 459 | REQUIRE(entity.get() == 2); 460 | REQUIRE(health == 1); 461 | } 462 | } 463 | AND_WHEN("Dividing Health with 2 using /=") { 464 | entity.get() /= 2; 465 | THEN("Health should be 1") { 466 | REQUIRE(entity.get() == 1); 467 | } 468 | } 469 | WHEN("Subtracting 2 Health with [health = health - 2]") { 470 | entity.get() = entity.get() - 2; 471 | THEN("Health should be 0") { 472 | REQUIRE(entity.get() == 0); 473 | } 474 | } 475 | WHEN("Subtracting 2 Health to an int variable") { 476 | int health = entity.get() - 2; 477 | THEN("Health should still be 0 and variable should be -2") { 478 | REQUIRE(entity.get() == 2); 479 | REQUIRE(health == 0); 480 | } 481 | } 482 | WHEN("Setting Health to a value") { 483 | entity.get() = 3; 484 | THEN("Health should be that value") { 485 | REQUIRE(entity.get() == 3); 486 | } 487 | } 488 | WHEN("Adding health with ++e.get()") { 489 | int health = ++entity.get(); 490 | THEN("returned value should be new value") { 491 | REQUIRE(health == 3); 492 | } 493 | } 494 | WHEN("Adding health with e.get()++") { 495 | int health = entity.get()++; 496 | THEN("Returned value should be old value") { 497 | REQUIRE(health == 2); 498 | } 499 | } 500 | WHEN("Subtracting health with --e.get()") { 501 | int health = --entity.get(); 502 | THEN("returned value should be new value") { 503 | REQUIRE(health == 1); 504 | } 505 | } 506 | WHEN("Subtracting health with e.get()--") { 507 | int health = entity.get()--; 508 | THEN("Returned value should be old value") { 509 | REQUIRE(health == 2); 510 | } 511 | } 512 | WHEN("Setting its Health to the same level as Mana") { 513 | entity.get() = entity.get(); 514 | THEN("Entity should have 10 Health") { 515 | REQUIRE(entity.get() == 10); 516 | } 517 | } 518 | WHEN("Comparing its Health with its Mana") { 519 | bool c = entity.get() == entity.get(); 520 | THEN("This should be false") { 521 | REQUIRE(!c); 522 | } 523 | } 524 | } 525 | GIVEN("An entity with wheels and health and mana of 1") { 526 | Entity entity = entities.create(); 527 | entity.add(); 528 | entity.add(1); 529 | entity.add(1); 530 | 531 | WHEN("Accessing Entity as Car and start driving") { 532 | Car &car = entity.as(); 533 | car.drive(1, 1); 534 | THEN("Car should be driving") { 535 | REQUIRE(entity.has()); 536 | REQUIRE(entity.get().x == 1); 537 | REQUIRE(entity.get().y == 1); 538 | } 539 | } 540 | WHEN("Assumeing Entity has Wheels, should work") { 541 | entity.assume().get(); 542 | } 543 | WHEN("Assumeing Entity has Hat, should not work"){ 544 | REQUIRE_THROWS(entity.assume()); 545 | } 546 | WHEN("Creating 2 new entities and fetching every Car") { 547 | entities.create(); 548 | entities.create(); 549 | auto cars = entities.fetch_every(); 550 | THEN("Number of cars should be 1, when using count method") { 551 | REQUIRE(cars.count() == 1); 552 | } 553 | THEN("Number of cars should be 1, when counting with for each loop") { 554 | int count = 0; 555 | for (Car car : cars) { 556 | (void) car; 557 | ++count; 558 | } 559 | REQUIRE(count == 1); 560 | } 561 | THEN("Number of cars should be 1, when counting with lambda, pass by value") { 562 | int count = 0; 563 | entities.fetch_every([&](Car car) { 564 | ++count; 565 | }); 566 | REQUIRE(count == 1); 567 | } 568 | THEN("Number of cars should be 1 when counting with lambda, pass by reference") { 569 | int count = 0; 570 | entities.fetch_every([&](Car &car) { 571 | ++count; 572 | }); 573 | REQUIRE(count == 1); 574 | } 575 | THEN("Number of entities with Wheels should be one and that entity should have 1 health and mana") { 576 | int count = 0; 577 | entities.with([&](Wheels &wheels, Health &health, Mana &mana) { 578 | ++count; 579 | REQUIRE(health == 1); 580 | REQUIRE(mana == 1); 581 | }); 582 | REQUIRE(count == 1); 583 | } 584 | THEN("Unpacking Wheels and entity should work") { 585 | int count = 0; 586 | entities.with([&](Wheels &wheels, Entity entity) { 587 | ++count; 588 | REQUIRE(&entity.get() == &wheels); 589 | }); 590 | REQUIRE(count == 1); 591 | } 592 | THEN("Getting Health and Mana using get should result in using different get functions") { 593 | int count = 0; 594 | for (auto e : entities.with()) { 595 | ++count; 596 | REQUIRE(e.get() == 1); 597 | REQUIRE(e.get() == 1); 598 | e.remove(); 599 | } 600 | REQUIRE(count == 1); 601 | } 602 | AND_WHEN("A change is made to some of the components as ref") { 603 | entities.with([](Mana &mana) { 604 | mana = 10; 605 | }); 606 | THEN("Change should be made") { 607 | REQUIRE(entity.get() == 10); 608 | } 609 | } 610 | AND_WHEN("A change is made to some of the components as copied value") { 611 | entities.with([](Mana mana) { 612 | mana = 10; 613 | }); 614 | THEN("Change should NOT be made") { 615 | REQUIRE(entity.get() != 10); 616 | } 617 | } 618 | } 619 | } 620 | WHEN("Creating a car using the EntityManager with speed 10,10") { 621 | auto car = entities.create(10, 10); 622 | THEN("Speed should be 10") { 623 | REQUIRE(car.get().x == 10); 624 | REQUIRE(car.get().y == 10); 625 | } 626 | } 627 | WHEN("Creating a car using the EntityManager without setting speed") { 628 | Car car = entities.create(); 629 | THEN("Car should be a car") { 630 | REQUIRE(car.is()); 631 | } 632 | THEN("Car should not be moving") { 633 | REQUIRE(!car.is_moving()); 634 | } 635 | AND_WHEN("Car starts driving") { 636 | car.drive(1, 1); 637 | THEN("Car should be driving") { 638 | REQUIRE(car.is_moving()); 639 | } 640 | } 641 | AND_WHEN("Removing Wheels") { 642 | car.remove(); 643 | THEN("Car is no longer a car") { 644 | REQUIRE(!car.is()); 645 | } 646 | } 647 | } 648 | WHEN("Creating a Character using standard constructor with name: TestCharacter, Height: 180, Weight: 80") { 649 | Character character = entities.create("TestCharacter", 180, 80); 650 | THEN("Name, height and weight should be correct") { 651 | REQUIRE(character.get() == "TestCharacter"); 652 | REQUIRE(character.get() == 180); 653 | REQUIRE(character.get() == 80); 654 | } 655 | } 656 | 657 | WHEN("Creating 1 entity with Health and mana") { 658 | Entity e1 = entities.create_with(10, 1); 659 | Entity e2 = entities.create_with(Health(8), Mana(5)); 660 | THEN("They should have health and mana") { 661 | REQUIRE(e1.has()); 662 | REQUIRE(e1.has()); 663 | REQUIRE(e1.get() == 10); 664 | REQUIRE(e1.get() == 1); 665 | 666 | REQUIRE(e2.has()); 667 | REQUIRE(e2.has()); 668 | REQUIRE(e2.get() == 8); 669 | REQUIRE(e2.get() == 5); 670 | } 671 | } 672 | 673 | WHEN("Creating 1 entity with Health and mana") { 674 | Entity entity = entities.create_with(); 675 | THEN("They should have health and mana") { 676 | REQUIRE(entity.has()); 677 | REQUIRE(entity.has()); 678 | REQUIRE(entity.get() == 0); 679 | REQUIRE(entity.get() == 0); 680 | } 681 | } 682 | 683 | // Testing so that entities are created at appropriate locations 684 | WHEN("Creating some entities with different components attached") { 685 | Entity e1 = entities.create(); 686 | Entity e2 = entities.create_with(); 687 | Entity e3 = entities.create(); 688 | Entity e4 = entities.create_with(10); 689 | Entity e5 = entities.create_with(1, 10); 690 | THEN("They should be placed in their appropriate locations") { 691 | REQUIRE(e1.id().index() == 0); 692 | REQUIRE(e2.id().index() == ECS_CACHE_LINE_SIZE); 693 | REQUIRE(e3.id().index() == 1); 694 | REQUIRE(e4.id().index() == ECS_CACHE_LINE_SIZE * 2); 695 | REQUIRE(e5.id().index() == 1 + ECS_CACHE_LINE_SIZE); 696 | } 697 | } 698 | 699 | WHEN("Adding 1 entity, 1 car, then 1 entity in sequence") { 700 | Entity e1 = entities.create(); 701 | Car c1 = entities.create(); 702 | Entity e2 = entities.create(); 703 | THEN("They should be assigned to indexes e1 = [0], car = [64] e2 = [1]") { 704 | REQUIRE(e1.id().index() == 0); 705 | REQUIRE(e2.id().index() == 1); 706 | REQUIRE(c1.id().index() == ECS_CACHE_LINE_SIZE); 707 | } 708 | } 709 | 710 | WHEN("Adding 1 entity, 1 car, then 64 entities, then 1 car in sequence") { 711 | Entity e1 = entities.create(); 712 | Car c1 = entities.create(); 713 | std::vector es = entities.create(ECS_CACHE_LINE_SIZE); 714 | Car c2 = entities.create(); 715 | THEN("They should be assigned to indexes e0 = [0], car = [64] e64 = [128] ()") { 716 | REQUIRE(e1.id().index() == 0); 717 | REQUIRE(c1.id().index() == ECS_CACHE_LINE_SIZE); 718 | REQUIRE(c2.id().index() == ECS_CACHE_LINE_SIZE + 1); 719 | REQUIRE(es.back().id().index() == ECS_CACHE_LINE_SIZE * 2); 720 | } 721 | } 722 | 723 | WHEN("Adding 1000 entities with, Health, and 1000 with Mana") { 724 | for (int i = 0; i < 1000; i++) { 725 | entities.create_with(); 726 | entities.create_with(); 727 | } 728 | THEN("They enitities with Health and Mana respectivly should be 1000") { 729 | REQUIRE(entities.with().count() == 1000); 730 | REQUIRE(entities.with().count() == 1000); 731 | } 732 | 733 | } 734 | 735 | //Testing UnallocatedEntity 736 | GIVEN("An UnallocatedEntity"){ 737 | Entity e = entities.create(); 738 | { 739 | UnallocatedEntity entity(entities); 740 | WHEN("Doing things with the Entity") { 741 | entity.set(1); 742 | entity.set(10); 743 | entity.set("Hoppsan"); 744 | THEN("Health should be 1") { 745 | REQUIRE(entity.get() == 1); 746 | REQUIRE(entity.get() == 10); 747 | REQUIRE(entity.get() == "Hoppsan"); 748 | } 749 | entity.remove(); 750 | THEN("Health should be 1") { 751 | REQUIRE(entity.get() == 1); 752 | REQUIRE(!entity.has()); 753 | REQUIRE(entity.get() == "Hoppsan"); 754 | } 755 | AND_WHEN("Adding components after it was allocated"){ 756 | Entity e = entity; 757 | e.set(10); 758 | THEN("New and old components should be retained"){ 759 | REQUIRE(e.get() == 1); 760 | REQUIRE(!e.has()); 761 | REQUIRE(e.get() == "Hoppsan"); 762 | REQUIRE(e.get() == 10); 763 | } 764 | } 765 | } 766 | entity.set(1); 767 | entity.set(10); 768 | entity.set("Hoppsan"); 769 | e = entity; 770 | } 771 | WHEN("Setting components and allocate Entity"){ 772 | THEN("Allocated Entity should have all the components"){ 773 | REQUIRE(e.get() == 1); 774 | REQUIRE(e.get() == 10); 775 | REQUIRE(e.get() == "Hoppsan"); 776 | } 777 | } 778 | 779 | } 780 | 781 | //Testing Systems 782 | GIVEN("A SystemManager") { 783 | SystemManager systems(entities); 784 | WHEN("Adding count car system and remove dead entities system") { 785 | systems.add(); 786 | systems.add(); 787 | THEN("Systems should exists") { 788 | REQUIRE(systems.exists()); 789 | REQUIRE(systems.exists()); 790 | } 791 | AND_WHEN("Removing one of them") { 792 | systems.remove(); 793 | THEN("That system should not exist anymore") { 794 | REQUIRE(!systems.exists()); 795 | } 796 | } 797 | AND_WHEN("Adding one dead entity, and calling update") { 798 | Entity e = entities.create(); 799 | e.add(-1); 800 | systems.update(0); 801 | THEN("Entity should be removed.") { 802 | REQUIRE(!e.is_valid()); 803 | REQUIRE(entities.count() == 0); 804 | } 805 | } 806 | } 807 | } 808 | } 809 | } -------------------------------------------------------------------------------- /test/ecs_performance.cpp: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------- 2 | /// Copyright (C) 2015 Robin Grönberg 3 | /// 4 | /// This program is free software: you can redistribute it and/or modify 5 | /// it under the terms of the GNU General Public License as published by 6 | /// the Free Software Foundation, either version 3 of the License, or 7 | /// (at your option) any later version. 8 | /// 9 | /// This program is distributed in the hope that it will be useful, 10 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | /// GNU General Public License for more details. 13 | /// 14 | /// You should have received a copy of the GNU General Public License 15 | /// along with this program. If not, see . 16 | 17 | #include 18 | #include 19 | 20 | #include "common/thirdparty/catch.hpp" 21 | 22 | #include "ecs.h" 23 | 24 | using namespace ecs; 25 | 26 | namespace { 27 | struct Wheels { 28 | int value; 29 | }; 30 | 31 | struct Door { 32 | int value; 33 | }; 34 | 35 | struct Hat { 36 | int i; 37 | }; 38 | 39 | struct Clothes { 40 | int i[16]; 41 | }; 42 | 43 | struct Car: EntityAlias { 44 | }; 45 | 46 | // Used to make sure the comparator does not optimize away my for-loop 47 | struct BaseFoo { 48 | virtual void bar() = 0; 49 | bool always_false = false; 50 | }; 51 | 52 | struct Foo: BaseFoo { 53 | virtual void bar() override { 54 | 55 | } 56 | }; 57 | } 58 | 59 | class Timer { 60 | public: 61 | Timer() { restart(); } 62 | ~Timer() { std::cout << "Time elapsed: " << elapsed() << std::endl << std::endl; } 63 | 64 | void restart() { _start = std::chrono::system_clock::now(); } 65 | double elapsed() { return std::chrono::duration(std::chrono::system_clock::now() - _start).count(); } 66 | private: 67 | std::chrono::time_point _start; 68 | }; 69 | 70 | TEST_CASE("TestEntityCreation") { 71 | int count = 10000000; 72 | EntityManager em; 73 | 74 | std::cout << "Creating " << count << " entities using create()" << std::endl; 75 | Timer t; 76 | for (int i = 0; i < count; ++i) { 77 | em.create(); 78 | } 79 | REQUIRE(em.count() == count); 80 | } 81 | 82 | TEST_CASE("TestEntityCreationMany") { 83 | int count = 10000000; 84 | EntityManager em; 85 | 86 | std::cout << "Creating " << count << " entities using create(int)" << std::endl; 87 | Timer t; 88 | auto new_entities = em.create(count); 89 | REQUIRE(em.count() == count); 90 | } 91 | 92 | TEST_CASE("TestEntityDestruction") { 93 | int count = 10000000; 94 | EntityManager em; 95 | auto entities = em.create(count); 96 | { 97 | std::cout << "Destroying " << count << " entities" << std::endl; 98 | Timer t; 99 | for (size_t i = 0; i < entities.size(); ++i) { 100 | entities[i].destroy(); 101 | } 102 | REQUIRE(em.count() == 0); 103 | } 104 | { 105 | std::cout << "Recreating after destroying " << count << " entities" << std::endl; 106 | Timer t; 107 | auto entities = em.create(count); 108 | } 109 | } 110 | 111 | SCENARIO("TestEntityIteration") { 112 | const int count = 10000000; 113 | EntityManager entities; 114 | for (int i = 0; i < count; i++) { 115 | entities.create_with(); 116 | } 117 | 118 | WHEN("Iterating using normal for-loop") { 119 | std::cout << "Iterating over " << count << " using normal for loop" << std::endl; 120 | BaseFoo *foo = new Foo(); 121 | { 122 | Timer t; 123 | for (int i = 0; i < count; ++i) { 124 | //we don't want to call dummy function. 125 | if (foo->always_false) { 126 | //useless function call to make sure the loop is not optimized away. 127 | foo->bar(); 128 | } 129 | } 130 | } 131 | } 132 | 133 | WHEN("Iterating manually with iterator") { 134 | std::cout << "Iterating over " << count << " using iterator manually" << std::endl; 135 | Timer t; 136 | auto it = entities.with().begin(); 137 | auto end = entities.with().end(); 138 | for (; it != end; ++it) { 139 | (void) it; 140 | } 141 | } 142 | 143 | WHEN("Iterating using with") { 144 | std::cout << "Iterating over " << count << " using with for-loop without unpacking" << std::endl; 145 | Timer t; 146 | for (auto e : entities.with()) { 147 | (void) e; 148 | } 149 | } 150 | 151 | WHEN("Iterating using with") { 152 | std::cout << "Iterating over " << count << " using with for-loop unpacking one component" << std::endl; 153 | Timer t; 154 | for (auto e : entities.with()) { 155 | e.get(); 156 | } 157 | } 158 | 159 | WHEN("Iterating using with") { 160 | std::cout << "Iterating over " << count << " using with lambda unpacking one component" << std::endl; 161 | Timer t; 162 | entities.with([](Wheels &wheels) { 163 | (void) wheels; 164 | }); 165 | } 166 | 167 | WHEN("Iterating using with") { 168 | std::cout << "Iterating over " << count << " using with for-loop unpacking two components" << std::endl; 169 | Timer t; 170 | for (auto e : entities.with()) { 171 | e.get(); 172 | e.get(); 173 | } 174 | } 175 | 176 | WHEN("Iterating using with") { 177 | std::cout << "Iterating over " << count << " using with lambda unpacking two components" << std::endl; 178 | Timer t; 179 | entities.with([](Wheels &wheels, Door &door) { 180 | (void) wheels; 181 | (void) door; 182 | }); 183 | } 184 | 185 | WHEN("Iterating using fetch_every") { 186 | std::cout << "Iterating over " << count << " using fetch_every for-loop" << std::endl; 187 | Timer t; 188 | for (auto e : entities.fetch_every()) { 189 | (void) e; 190 | } 191 | } 192 | 193 | WHEN("Iterating using fetch_every") { 194 | std::cout << "Iterating over " << count << " using fetch_every lambda" << std::endl; 195 | Timer t; 196 | entities.fetch_every([](Car &car) { 197 | (void) car; 198 | }); 199 | } 200 | } 201 | 202 | SCENARIO("TestEntityIterationForContinousMemory hard code") { 203 | const int count = 10000000; 204 | Wheels* wheels = new Wheels[count]; 205 | 206 | WHEN("Iterating over entities with doors (hard code)") { 207 | std::cout << "Iterating over " << count << " using with Doors continous in memory (hard code)" << std::endl; 208 | BaseFoo *foo = new Foo(); 209 | { 210 | Timer t; 211 | for (int i = 0; i < count; ++i) { 212 | if(foo->always_false){ 213 | foo->bar(); 214 | } 215 | (void)wheels[i].value; 216 | } 217 | } 218 | } 219 | } 220 | 221 | SCENARIO("TestEntityIterationForSplittedMemory hard code") { 222 | const int count = 10000000; 223 | Wheels* wheels = new Wheels[count * 20]; 224 | 225 | WHEN("Iterating over entities with doors (hard code)") { 226 | std::cout << "Iterating over " << count << " using with Doors split in memory (hard code)" << std::endl; 227 | BaseFoo *foo = new Foo(); 228 | { 229 | Timer t; 230 | for (int i = 0; i < count * 20; i+=20) { 231 | if(foo->always_false){ 232 | foo->bar(); 233 | } 234 | (void)wheels[i].value; 235 | } 236 | } 237 | } 238 | } 239 | 240 | SCENARIO("TestEntityIterationForContinousMemory") { 241 | int count = 10000000; 242 | EntityManager entities; 243 | 244 | for (int i = 0; i < count / 16; ++i) { 245 | for (int j = 0; j < 15; ++j) { 246 | entities.create_with(); 247 | } 248 | entities.create_with(); 249 | } 250 | REQUIRE(entities.with().count() == count / 16); 251 | 252 | WHEN("Iterating over entities with doors") { 253 | std::cout << "Iterating over " << count << " using with Doors continous in memory" << std::endl; 254 | { 255 | Timer t; 256 | entities.with([](Clothes &clothes) { clothes.i[0] = 0; }); 257 | } 258 | } 259 | } 260 | 261 | SCENARIO("TestEntityIterationForContinousMemory Unallocated") { 262 | int count = 10000000; 263 | EntityManager entities; 264 | for (int i = 0; i < count / 16; ++i) { 265 | for (int j = 0; j < 15; ++j) { 266 | entities.create().add(); 267 | } 268 | entities.create().add(); 269 | } 270 | REQUIRE(entities.with().count() == count / 16); 271 | 272 | WHEN("Iterating over entities with doors") { 273 | std::cout << "Iterating over " << count << " using with Doors, continous in memory (unallocated)" << std::endl; 274 | { 275 | Timer t; 276 | entities.with([](Clothes &clothes) { clothes.i[0] = 0; }); 277 | } 278 | } 279 | } 280 | 281 | SCENARIO("TestEntityIterationForSplittedMemory") { 282 | int count = 10000000; 283 | EntityManager entities; 284 | for (int i = 0; i < count / 16; ++i) { 285 | for (int j = 0; j < 15; ++j) { 286 | Entity entity = entities.create(); 287 | entity.add(); 288 | } 289 | Entity entity = entities.create(); 290 | entity.add(); 291 | } 292 | REQUIRE(entities.with().count() == count / 16); 293 | 294 | WHEN("Iterating over entities with doors") { 295 | std::cout << "Iterating over " << count << " using with Doors, split in memory" << std::endl; 296 | { 297 | Timer t; 298 | entities.with([](Clothes &clothes) { clothes.i[0] = 0; }); 299 | } 300 | } 301 | } 302 | 303 | SCENARIO("TestEntityCreationUnallocatedEntity") { 304 | int count = 10000000; 305 | EntityManager entities; 306 | std::cout << "Creating " << count << " with with Doors, Unallocated Entity" << std::endl; 307 | { 308 | Timer t; 309 | for (int i = 0; i < count; ++i) { 310 | entities.create().add(); 311 | } 312 | } 313 | } 314 | 315 | SCENARIO("TestEntityCreationEntity") { 316 | int count = 10000000; 317 | EntityManager entities; 318 | std::cout << "Creating " << count << " with with Doors, Entity" << std::endl; 319 | { 320 | Timer t; 321 | for (int i = 0; i < count; ++i) { 322 | Entity e = entities.create(); 323 | e.add(); 324 | } 325 | } 326 | } 327 | 328 | SCENARIO("TestEntityCreation_with_Entity") { 329 | int count = 10000000; 330 | EntityManager entities; 331 | std::cout << "Creating " << count << " with with Doors, Entity create_with" << std::endl; 332 | { 333 | Timer t; 334 | for (int i = 0; i < count; ++i) { 335 | entities.create_with(); 336 | } 337 | } 338 | } --------------------------------------------------------------------------------