├── EntityFu.cpp ├── EntityFu.h ├── EntityFu.xcodeproj └── project.pbxproj ├── LICENSE ├── README.md └── main.cpp /EntityFu.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | /// [EntityFu](https://github.com/NatWeiss/EntityFu) 3 | /// A simple, fast entity component system written in C++. 4 | /// Under the MIT license. 5 | /// 6 | 7 | #include "EntityFu.h" 8 | #include 9 | using namespace std; 10 | 11 | /// Turn this on to have a faster yet riskier ECS. 12 | #define kTrustPointers 0 13 | 14 | /// Log macro. 15 | #ifndef LogV 16 | #ifndef NDEBUG 17 | #include 18 | #define LogV(verbosity, minVerbosity, ...) do {if ((verbosity) >= (minVerbosity)) {printf(__VA_ARGS__); printf("\n");}} while (0) 19 | #else 20 | #define LogV(...) do {} while (0) 21 | #endif 22 | #endif 23 | 24 | /// Assert macro. 25 | #ifndef Assert 26 | #ifndef NDEBUG 27 | #define Assert(condition, format, ...) {if(!condition) throw format;} 28 | #else 29 | #define Assert(...) do {} while (0) 30 | #endif 31 | #endif 32 | 33 | /// Turn this to 1 or 2 to debug the ECS. 34 | /// 1 == log creation, 2 == also log deletion, 3 == also log component add/remove, 4 == also log component totals. 35 | static int verbosity = 0; 36 | 37 | /// Static pointers to the ECS data. 38 | static bool* entities = nullptr; 39 | static Entity::Component*** components = nullptr; 40 | static vector* componentEids = nullptr; 41 | 42 | static void log(Cid cid) 43 | { 44 | auto n = Entity::count(cid); 45 | auto& eids = Entity::getAll(cid); 46 | if (eids.size() > 0) 47 | LogV(verbosity, 4, " Cid %u has %d entities ranging from %u to %u", cid, n, eids.front(), eids.back()); 48 | } 49 | 50 | void Entity::alloc() 51 | { 52 | if (components != nullptr) 53 | return; 54 | LogV(verbosity, 1, "Allocing entities"); 55 | 56 | // allocate entities 57 | entities = new bool[kMaxEntities]; 58 | for (Eid eid = 0; eid < kMaxEntities; ++eid) 59 | entities[eid] = false; 60 | 61 | // allocate components 62 | auto max = Component::numCids; 63 | components = new Component**[max]; 64 | componentEids = new vector[Component::numCids]; 65 | for (Cid cid = 0; cid < max; cid++) 66 | { 67 | // allocate component array 68 | components[cid] = new Component*[kMaxEntities]; 69 | 70 | // zero component pointers 71 | for (Eid eid = 0; eid < kMaxEntities; eid++) 72 | components[cid][eid] = nullptr; 73 | } 74 | } 75 | 76 | void Entity::dealloc() 77 | { 78 | LogV(verbosity, 1, "Deallocing entities"); 79 | 80 | if (components != nullptr) 81 | { 82 | Entity::destroyAll(); 83 | for (Cid cid = 0; cid < Component::numCids; cid++) 84 | if (components[cid] != nullptr) 85 | delete [] components[cid]; 86 | delete [] components; 87 | } 88 | 89 | if (componentEids != nullptr) 90 | delete [] componentEids; 91 | 92 | if (entities != nullptr) 93 | delete [] entities; 94 | 95 | entities = nullptr; 96 | components = nullptr; 97 | componentEids = nullptr; 98 | } 99 | 100 | Eid Entity::create() 101 | { 102 | // auto allocate 103 | Entity::alloc(); 104 | 105 | Eid eid = 1; 106 | for (; eid < kMaxEntities && entities[eid]; ++eid) 107 | { 108 | } 109 | 110 | if (eid < 1 || eid >= kMaxEntities) 111 | { 112 | Assert(false, "Maximum number of entities reached!"); 113 | eid = 0; 114 | } 115 | else 116 | { 117 | entities[eid] = true; 118 | LogV(verbosity, 1, "Entity %u created", eid); 119 | } 120 | 121 | return eid; 122 | } 123 | 124 | void Entity::destroyNow(Eid eid) 125 | { 126 | if (eid == 0) 127 | return; 128 | LogV(verbosity, 1, "Entity %u being destroyed", eid); 129 | 130 | for (Cid cid = 0; cid < Component::numCids; cid++) 131 | Entity::removeComponent(cid, eid); 132 | entities[eid] = false; 133 | } 134 | 135 | void Entity::destroyAll() 136 | { 137 | unsigned count = 0; 138 | auto oldVerbosity = verbosity; 139 | verbosity = 0; 140 | for (Eid eid = 1; eid < kMaxEntities; ++eid) 141 | { 142 | if (entities[eid]) 143 | { 144 | Entity::destroyNow(eid); 145 | count++; 146 | } 147 | } 148 | verbosity = oldVerbosity; 149 | LogV(verbosity, 1, "%u entities destroyed", count); 150 | } 151 | 152 | void Entity::addComponent(Cid cid, Eid eid, Component* c) 153 | { 154 | if (c == nullptr) 155 | return; 156 | if (eid >= kMaxEntities || !entities[eid] || cid >= Component::numCids) 157 | { 158 | Assert(false, "Invalid eid %u or cid %u", eid, cid); 159 | return; 160 | } 161 | 162 | if (verbosity >= 4) 163 | log(cid); 164 | LogV(verbosity, 3, " Adding component cid %u eid %u (%x)", cid, eid, (int)(long)c); 165 | 166 | // if component already added, delete old one 167 | if (components[cid][eid] != nullptr) 168 | Entity::removeComponent(cid, eid); 169 | 170 | // pointers to components are stored in the map 171 | // (components must be allocated with new, not stack objects) 172 | components[cid][eid] = c; 173 | 174 | // store component eids 175 | componentEids[cid].push_back(eid); 176 | 177 | if (verbosity >= 4) 178 | log(cid); 179 | } 180 | 181 | void Entity::removeComponent(Cid cid, Eid eid) 182 | { 183 | if (eid >= kMaxEntities || !entities[eid] || cid >= Component::numCids) 184 | { 185 | Assert(false, "Invalid eid %u or cid %u", eid, cid); 186 | return; 187 | } 188 | 189 | // get pointer 190 | auto ptr = components[cid][eid]; 191 | if (ptr == nullptr) 192 | return; 193 | 194 | if (verbosity >= 4) 195 | log(cid); 196 | LogV(verbosity, 3, " Removing component cid %u eid %u (%x)", cid, eid, (int)(long)ptr); 197 | 198 | // pointers to components are deleted 199 | delete ptr; 200 | 201 | // erase the component pointer 202 | components[cid][eid] = nullptr; 203 | 204 | // update component eids 205 | auto& eids = componentEids[cid]; 206 | auto it = find(eids.begin(), eids.end(), eid); 207 | if (it != eids.end()) 208 | it = eids.erase(it); 209 | 210 | if (verbosity >= 4) 211 | log(cid); 212 | } 213 | 214 | Entity::Component* Entity::getComponent(Cid cid, Eid eid) 215 | { 216 | #if (kTrustPointers == 0) 217 | if (eid < kMaxEntities && cid < Component::numCids) 218 | { 219 | #endif 220 | return components[cid][eid]; 221 | 222 | #if (kTrustPointers == 0) 223 | } 224 | return nullptr; 225 | #endif 226 | } 227 | 228 | const vector& Entity::getAll(Cid cid) 229 | { 230 | if (componentEids != nullptr && cid < Component::numCids) 231 | return componentEids[cid]; 232 | static vector blankEids; 233 | return blankEids; 234 | } 235 | 236 | unsigned Entity::count() 237 | { 238 | int ret = 0; 239 | if (entities != nullptr) 240 | { 241 | for (Eid eid = 1; eid < kMaxEntities; ++eid) 242 | if (entities[eid]) 243 | ++ret; 244 | } 245 | return ret; 246 | } 247 | 248 | unsigned Entity::count(Cid cid) 249 | { 250 | return (unsigned)Entity::getAll(cid).size(); 251 | } 252 | 253 | bool Entity::exists(Eid eid) 254 | { 255 | return entities != nullptr && entities[eid]; 256 | } 257 | 258 | // 259 | // Entity::Component 260 | // 261 | 262 | Entity::Component::~Component() 263 | { 264 | } 265 | 266 | bool Entity::Component::full() const 267 | { 268 | return !this->empty(); 269 | } 270 | 271 | // 272 | // System 273 | // 274 | 275 | void System::tick(double fixedDelta) 276 | { 277 | } 278 | 279 | void System::animate(double delta, double tickPercent) 280 | { 281 | } 282 | 283 | 284 | -------------------------------------------------------------------------------- /EntityFu.h: -------------------------------------------------------------------------------- 1 | /// 2 | /// [EntityFu](https://github.com/NatWeiss/EntityFu) 3 | /// A simple, fast entity component system written in C++. 4 | /// Under the MIT license. 5 | /// 6 | 7 | #pragma once 8 | #include 9 | 10 | /// An `Eid` is an entity ID. 11 | typedef unsigned Eid; 12 | 13 | /// A `Cid` is a component ID. 14 | typedef unsigned Cid; 15 | 16 | namespace Entity 17 | { 18 | struct Component; 19 | 20 | /// The maximum number of entities. Increase this if you need more. 21 | enum {kMaxEntities = 8192}; 22 | 23 | /// Allocate the memory for entities and components. Can call this manually or let it allocate automatically. 24 | void alloc(); 25 | 26 | /// Deallocate the memory for entities and components. Only do this when you no longer need the ECS. 27 | void dealloc(); 28 | 29 | /// Create an entity and return the `Eid`. 30 | Eid create(); 31 | 32 | /// Return a count of all entities. 33 | unsigned count(); 34 | 35 | /// Return true if the entity has been created. 36 | bool exists(Eid eid); 37 | 38 | /// Destroy an entity and all its components right now. 39 | void destroyNow(Eid eid); 40 | 41 | /// Destroy all entities and components right now. 42 | void destroyAll(); 43 | 44 | /// Component-related methods that require a `Cid`. 45 | /// The templated versions of these methods do not require a `Cid`, yet incur an extra function call of overhead. 46 | void addComponent(Cid cid, Eid eid, Component* c); 47 | void removeComponent(Cid cid, Eid eid); 48 | Component* getComponent(Cid cid, Eid eid); 49 | const std::vector& getAll(Cid cid); 50 | unsigned count(Cid cid); 51 | 52 | /// Add the given component to the given entity. 53 | /// Note that components must be allocated with new. 54 | template inline static void addComponent(Eid eid, ComponentClass* c) 55 | { 56 | return Entity::addComponent(ComponentClass::cid, eid, c); 57 | } 58 | 59 | /// Remove a component from an entity. 60 | template inline static void removeComponent(Eid eid) 61 | { 62 | return Entity::removeComponent(ComponentClass::cid, eid); 63 | } 64 | 65 | /// Get a reference to a component. 66 | /// Warning, if the Eid does not exist a blank static component will be returned, 67 | /// so be sure to check if it is empty before writing any data to it or else 68 | /// the blank static component will no longer be empty and the Earth will implode. 69 | template inline static ComponentClass& get(Eid eid) 70 | { 71 | auto p = static_cast(Entity::getComponent(ComponentClass::cid, eid)); 72 | if (p != nullptr) 73 | return *p; 74 | static ComponentClass s; 75 | return s; 76 | } 77 | 78 | /// Get a pointer to a component. 79 | template inline static ComponentClass* getPointer(Eid eid) 80 | { 81 | return static_cast(Entity::getComponent(ComponentClass::cid, eid)); 82 | } 83 | 84 | /// Get a vector of all Eids for the given component class. 85 | template inline static const std::vector& getAll() 86 | { 87 | return Entity::getAll(ComponentClass::cid); 88 | } 89 | 90 | /// Count all the entities with the given component class. 91 | template inline static unsigned count() 92 | { 93 | return Entity::count(ComponentClass::cid); 94 | } 95 | 96 | /// A utility method for `Entity::create(...)`. 97 | /// The final call to `addComponents`. 98 | template static void addComponents(Eid eid, C* c) 99 | { 100 | Entity::addComponent(C::cid, eid, c); 101 | } 102 | 103 | /// A utility method for `Entity::create(...)`. 104 | /// The variadic template version of `addComponents`. 105 | template static void addComponents(Eid eid, C* c, Args... args) 106 | { 107 | Entity::addComponent(C::cid, eid, c); 108 | Entity::addComponents(eid, args...); 109 | } 110 | 111 | /// Create an entity with some components. 112 | /// Convenience method which calls `Entity::create()` and `Entity::addComponent`. 113 | template static Eid create(Args... args) 114 | { 115 | auto eid = Entity::create(); 116 | Entity::addComponents(eid, args...); 117 | return eid; 118 | } 119 | 120 | }; 121 | 122 | /// 123 | /// Component 124 | /// 125 | /// Inherit your game's components from this class. 126 | /// Give your component some data and be sure to implement the `empty` method. 127 | /// Also give your component class a static member `cid` of type `Cid` and increment `Entity::Component::numCids`. 128 | /// See the example code for details. 129 | /// 130 | struct Entity::Component 131 | { 132 | virtual ~Component(); 133 | virtual bool empty() const = 0; 134 | virtual bool full() const; 135 | static Cid numCids; 136 | // static Cid cid; 137 | }; 138 | 139 | /// 140 | /// Convenience macro to get a reference to a component or else run some code. 141 | /// Example: Entity__get(eid, health, HealthComponent, continue); 142 | /// 143 | #define Entity__get(componentClass, eid, varName, orElseCode) \ 144 | auto& (varName) = Entity::get((eid)); \ 145 | if ((varName).empty()) orElseCode 146 | 147 | /// 148 | /// System 149 | /// 150 | /// Just implement either `tick` or `animate` or both. 151 | /// Feel free to change this per your timestep implementation. 152 | /// `Ent` is a convenience struct which gets all the most often used components for an entity by reference, 153 | /// hence its implementation is based on your system design. 154 | /// 155 | class System 156 | { 157 | public: 158 | struct Ent; 159 | 160 | static void tick(double fixedDelta); 161 | static void animate(double delta, double tickPercent); 162 | }; 163 | 164 | 165 | -------------------------------------------------------------------------------- /EntityFu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 419BC9B31B0ECBF6007C6403 /* EntityFu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 419BC9B01B0ECBF6007C6403 /* EntityFu.cpp */; }; 11 | 419BC9B41B0ECBF6007C6403 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 419BC9B21B0ECBF6007C6403 /* main.cpp */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 419BC99A1B0EBA12007C6403 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 419BC99C1B0EBA12007C6403 /* EntityFu */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = EntityFu; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 419BC9AE1B0ECBEF007C6403 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 29 | 419BC9AF1B0ECBEF007C6403 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 30 | 419BC9B01B0ECBF6007C6403 /* EntityFu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EntityFu.cpp; sourceTree = ""; }; 31 | 419BC9B11B0ECBF6007C6403 /* EntityFu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EntityFu.h; sourceTree = ""; }; 32 | 419BC9B21B0ECBF6007C6403 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 419BC9991B0EBA12007C6403 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 419BC9931B0EBA12007C6403 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 419BC9AE1B0ECBEF007C6403 /* LICENSE */, 50 | 419BC9AF1B0ECBEF007C6403 /* README.md */, 51 | 419BC9B11B0ECBF6007C6403 /* EntityFu.h */, 52 | 419BC9B01B0ECBF6007C6403 /* EntityFu.cpp */, 53 | 419BC9B21B0ECBF6007C6403 /* main.cpp */, 54 | 419BC99D1B0EBA12007C6403 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 419BC99D1B0EBA12007C6403 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 419BC99C1B0EBA12007C6403 /* EntityFu */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | /* End PBXGroup section */ 67 | 68 | /* Begin PBXNativeTarget section */ 69 | 419BC99B1B0EBA12007C6403 /* EntityFu */ = { 70 | isa = PBXNativeTarget; 71 | buildConfigurationList = 419BC9A31B0EBA13007C6403 /* Build configuration list for PBXNativeTarget "EntityFu" */; 72 | buildPhases = ( 73 | 419BC9981B0EBA12007C6403 /* Sources */, 74 | 419BC9991B0EBA12007C6403 /* Frameworks */, 75 | 419BC99A1B0EBA12007C6403 /* CopyFiles */, 76 | ); 77 | buildRules = ( 78 | ); 79 | dependencies = ( 80 | ); 81 | name = EntityFu; 82 | productName = EntityFu; 83 | productReference = 419BC99C1B0EBA12007C6403 /* EntityFu */; 84 | productType = "com.apple.product-type.tool"; 85 | }; 86 | /* End PBXNativeTarget section */ 87 | 88 | /* Begin PBXProject section */ 89 | 419BC9941B0EBA12007C6403 /* Project object */ = { 90 | isa = PBXProject; 91 | attributes = { 92 | LastUpgradeCheck = 0730; 93 | ORGANIZATIONNAME = "Wizard Fu, Inc."; 94 | TargetAttributes = { 95 | 419BC99B1B0EBA12007C6403 = { 96 | CreatedOnToolsVersion = 6.2; 97 | }; 98 | }; 99 | }; 100 | buildConfigurationList = 419BC9971B0EBA12007C6403 /* Build configuration list for PBXProject "EntityFu" */; 101 | compatibilityVersion = "Xcode 3.2"; 102 | developmentRegion = English; 103 | hasScannedForEncodings = 0; 104 | knownRegions = ( 105 | en, 106 | ); 107 | mainGroup = 419BC9931B0EBA12007C6403; 108 | productRefGroup = 419BC99D1B0EBA12007C6403 /* Products */; 109 | projectDirPath = ""; 110 | projectRoot = ""; 111 | targets = ( 112 | 419BC99B1B0EBA12007C6403 /* EntityFu */, 113 | ); 114 | }; 115 | /* End PBXProject section */ 116 | 117 | /* Begin PBXSourcesBuildPhase section */ 118 | 419BC9981B0EBA12007C6403 /* Sources */ = { 119 | isa = PBXSourcesBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 419BC9B41B0ECBF6007C6403 /* main.cpp in Sources */, 123 | 419BC9B31B0ECBF6007C6403 /* EntityFu.cpp in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | 419BC9A11B0EBA13007C6403 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 135 | CLANG_CXX_LIBRARY = "libc++"; 136 | CLANG_ENABLE_MODULES = YES; 137 | CLANG_ENABLE_OBJC_ARC = YES; 138 | CLANG_WARN_BOOL_CONVERSION = YES; 139 | CLANG_WARN_CONSTANT_CONVERSION = YES; 140 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 141 | CLANG_WARN_EMPTY_BODY = YES; 142 | CLANG_WARN_ENUM_CONVERSION = YES; 143 | CLANG_WARN_INT_CONVERSION = YES; 144 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 145 | CLANG_WARN_UNREACHABLE_CODE = YES; 146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 147 | COPY_PHASE_STRIP = NO; 148 | ENABLE_STRICT_OBJC_MSGSEND = YES; 149 | GCC_C_LANGUAGE_STANDARD = gnu99; 150 | GCC_DYNAMIC_NO_PIC = NO; 151 | GCC_OPTIMIZATION_LEVEL = 0; 152 | GCC_PREPROCESSOR_DEFINITIONS = ( 153 | "DEBUG=1", 154 | "$(inherited)", 155 | ); 156 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 157 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 158 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 159 | GCC_WARN_UNDECLARED_SELECTOR = YES; 160 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 161 | GCC_WARN_UNUSED_FUNCTION = YES; 162 | GCC_WARN_UNUSED_VARIABLE = YES; 163 | MTL_ENABLE_DEBUG_INFO = YES; 164 | ONLY_ACTIVE_ARCH = YES; 165 | SDKROOT = macosx; 166 | }; 167 | name = Debug; 168 | }; 169 | 419BC9A21B0EBA13007C6403 /* Release */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | ALWAYS_SEARCH_USER_PATHS = NO; 173 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 174 | CLANG_CXX_LIBRARY = "libc++"; 175 | CLANG_ENABLE_MODULES = YES; 176 | CLANG_ENABLE_OBJC_ARC = YES; 177 | CLANG_WARN_BOOL_CONVERSION = YES; 178 | CLANG_WARN_CONSTANT_CONVERSION = YES; 179 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 180 | CLANG_WARN_EMPTY_BODY = YES; 181 | CLANG_WARN_ENUM_CONVERSION = YES; 182 | CLANG_WARN_INT_CONVERSION = YES; 183 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 184 | CLANG_WARN_UNREACHABLE_CODE = YES; 185 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 186 | COPY_PHASE_STRIP = NO; 187 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 188 | ENABLE_NS_ASSERTIONS = NO; 189 | ENABLE_STRICT_OBJC_MSGSEND = YES; 190 | GCC_C_LANGUAGE_STANDARD = gnu99; 191 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 193 | GCC_WARN_UNDECLARED_SELECTOR = YES; 194 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 195 | GCC_WARN_UNUSED_FUNCTION = YES; 196 | GCC_WARN_UNUSED_VARIABLE = YES; 197 | MTL_ENABLE_DEBUG_INFO = NO; 198 | SDKROOT = macosx; 199 | }; 200 | name = Release; 201 | }; 202 | 419BC9A41B0EBA13007C6403 /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | PRODUCT_NAME = "$(TARGET_NAME)"; 206 | }; 207 | name = Debug; 208 | }; 209 | 419BC9A51B0EBA13007C6403 /* Release */ = { 210 | isa = XCBuildConfiguration; 211 | buildSettings = { 212 | PRODUCT_NAME = "$(TARGET_NAME)"; 213 | }; 214 | name = Release; 215 | }; 216 | /* End XCBuildConfiguration section */ 217 | 218 | /* Begin XCConfigurationList section */ 219 | 419BC9971B0EBA12007C6403 /* Build configuration list for PBXProject "EntityFu" */ = { 220 | isa = XCConfigurationList; 221 | buildConfigurations = ( 222 | 419BC9A11B0EBA13007C6403 /* Debug */, 223 | 419BC9A21B0EBA13007C6403 /* Release */, 224 | ); 225 | defaultConfigurationIsVisible = 0; 226 | defaultConfigurationName = Release; 227 | }; 228 | 419BC9A31B0EBA13007C6403 /* Build configuration list for PBXNativeTarget "EntityFu" */ = { 229 | isa = XCConfigurationList; 230 | buildConfigurations = ( 231 | 419BC9A41B0EBA13007C6403 /* Debug */, 232 | 419BC9A51B0EBA13007C6403 /* Release */, 233 | ); 234 | defaultConfigurationIsVisible = 0; 235 | defaultConfigurationName = Release; 236 | }; 237 | /* End XCConfigurationList section */ 238 | }; 239 | rootObject = 419BC9941B0EBA12007C6403 /* Project object */; 240 | } 241 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Wizard Fu, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | EntityFu 3 | -------- 4 | 5 | A simple, fast entity component system written in C++. 6 | Originally built for the game [Songbringer](http://songbringer.com) and adapted for release. 7 | 8 | Only two files need to be included in your game project: `EntityFu.h` and `EntityFu.cpp`. 9 | 10 | See `main.cpp` for example code. 11 | 12 | Basically: 13 | - An entity is simply just an integer ID. 14 | - Components are pure data representing aspects of an entity. 15 | - Systems are where the code goes to operate on components. 16 | 17 | Here's an [intro to entity component systems](http://www.raywenderlich.com/24878/introduction-to-component-based-architecture-in-games). 18 | 19 | 20 | Ports 21 | ----- 22 | 23 | [@bwoogie](https://twitter.com/bwoogie) ported [Entity Fu to C#](https://github.com/bwoogie/EntityFu) 24 | 25 | 26 | License 27 | ------- 28 | 29 | EntityFu is licensed under the MIT license. 30 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | /// [EntityFu](https://github.com/NatWeiss/EntityFu) 3 | /// A simple, fast entity component system written in C++. 4 | /// Under the MIT license. 5 | /// 6 | 7 | #include "EntityFu.h" 8 | #include 9 | 10 | #ifdef _WIN32 11 | #include 12 | #define _sleep(ms) Sleep(ms) 13 | #else 14 | #include 15 | #define _sleep(ms) usleep(ms * 1000) 16 | #endif 17 | 18 | using namespace std; 19 | 20 | /// An example component. 21 | struct HealthComponent : Entity::Component 22 | { 23 | int hp, maxHP; 24 | 25 | HealthComponent(int _hp, int _maxHP) 26 | { 27 | hp = _hp; 28 | maxHP = _maxHP; 29 | } 30 | 31 | HealthComponent() : HealthComponent(0, 0) {} 32 | 33 | virtual bool empty() const {return maxHP == 0;} 34 | 35 | static Cid cid; 36 | }; 37 | 38 | /// Assign unique class IDs from 0 to `numClassIds` - 1 39 | static Cid _id = 0; 40 | Cid HealthComponent::cid = _id++; 41 | Cid Entity::Component::numCids = _id; // also critical to assign `numClassIds` 42 | 43 | /// An example `Ent`. 44 | struct System::Ent 45 | { 46 | Eid id; 47 | HealthComponent& health; 48 | /// Add more components your systems will use frequently 49 | 50 | Ent(Eid _id) : 51 | health(Entity::get(_id)), 52 | id(_id) 53 | {} 54 | }; 55 | 56 | /// An example system. 57 | struct HealthSystem : System 58 | { 59 | static void tick(double fixedDelta) 60 | { 61 | // create a copy of the vector for all health components so we 62 | // don't cause an assertion failure: "vector iterator not incrementable" 63 | // for more speed you can instead loop over a reference as long as you schedule 64 | // Entity::destroyNow for after the loop 65 | auto all = Entity::getAll(); 66 | 67 | // for this example, just decrement all health components each tick 68 | for (auto eid : all) 69 | { 70 | Ent e(eid); 71 | 72 | // this is overly pragmatic, but you get the drift of how to check if a component is valid 73 | if (e.health.empty()) 74 | continue; 75 | 76 | // decrement 77 | e.health.hp--; 78 | if (e.health.hp < 0) 79 | e.health.hp = 0; 80 | cout << "Entity " << (int)e.id << " has " << e.health.hp << "/" << e.health.maxHP << " hit points." << endl; 81 | 82 | // destroy entity if zero health 83 | if (e.health.hp <= 0) 84 | Entity::destroyNow(eid); 85 | } 86 | } 87 | }; 88 | 89 | int main(int argc, const char * argv[]) 90 | { 91 | // create some entities 92 | Entity::create(new HealthComponent(100, 100)); 93 | Entity::create(new HealthComponent(7, 7)); 94 | 95 | // run the system 96 | while (Entity::count()) 97 | { 98 | HealthSystem::tick(0.1); 99 | _sleep(100); 100 | } 101 | 102 | Entity::dealloc(); 103 | cout << "Goodbye, World!\n"; 104 | return 0; 105 | } 106 | 107 | 108 | --------------------------------------------------------------------------------