├── .gitignore ├── Editor.meta ├── Editor ├── Unity.LazyECS.Editor.asmdef ├── Unity.LazyECS.Editor.asmdef.meta ├── WorldDebugger.cs └── WorldDebugger.cs.meta ├── Icon.meta ├── Icon ├── lazyecsworlddebugger.png └── lazyecsworlddebugger.png.meta ├── Images.meta ├── Images ├── worlddebugger.png └── worlddebugger.png.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── ComponentLookup.cs ├── ComponentLookup.cs.meta ├── Entity.cs ├── Entity.cs.meta ├── Feature.cs ├── Feature.cs.meta ├── Group.cs ├── Group.cs.meta ├── ICleanupSystem.cs ├── ICleanupSystem.cs.meta ├── IComponent.cs ├── IComponent.cs.meta ├── IEntity.cs ├── IEntity.cs.meta ├── IEventSystem.cs ├── IEventSystem.cs.meta ├── IFeature.cs ├── IFeature.cs.meta ├── IGroup.cs ├── IGroup.cs.meta ├── IInitializeSystem.cs ├── IInitializeSystem.cs.meta ├── ISystem.cs ├── ISystem.cs.meta ├── ITeardownSystem.cs ├── ITeardownSystem.cs.meta ├── IUpdateSystem.cs ├── IUpdateSystem.cs.meta ├── IWorld.cs ├── IWorld.cs.meta ├── MonoBehaviourSingleton.cs ├── MonoBehaviourSingleton.cs.meta ├── SimulationController.cs ├── SimulationController.cs.meta ├── Systems.cs ├── Systems.cs.meta ├── Unity.LazyECS.asmdef ├── Unity.LazyECS.asmdef.meta ├── World.cs └── World.cs.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ 6 | 7 | # Common IntelliJ Platform excludes 8 | 9 | .idea 10 | 11 | # User specific 12 | **/.idea/**/workspace.xml 13 | **/.idea/**/tasks.xml 14 | **/.idea/shelf/* 15 | **/.idea/dictionaries 16 | **/.idea/httpRequests/ 17 | 18 | # Sensitive or high-churn files 19 | **/.idea/**/dataSources/ 20 | **/.idea/**/dataSources.ids 21 | **/.idea/**/dataSources.xml 22 | **/.idea/**/dataSources.local.xml 23 | **/.idea/**/sqlDataSources.xml 24 | **/.idea/**/dynamic.xml 25 | 26 | # Rider 27 | # Rider auto-generates .iml files, and contentModel.xml 28 | **/.idea/**/*.iml 29 | **/.idea/**/contentModel.xml 30 | **/.idea/**/modules.xml 31 | 32 | *.suo 33 | *.user 34 | .vs/ 35 | [Bb]in/ 36 | [Oo]bj/ 37 | _UpgradeReport_Files/ 38 | [Pp]ackages/ 39 | 40 | Thumbs.db 41 | Desktop.ini 42 | .DS_Store 43 | 44 | ## Ignore Visual Studio temporary files, build results, and 45 | ## files generated by popular Visual Studio add-ons. 46 | ## 47 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 48 | 49 | # User-specific files 50 | *.rsuser 51 | *.suo 52 | *.user 53 | *.userosscache 54 | *.sln.docstates 55 | 56 | # User-specific files (MonoDevelop/Xamarin Studio) 57 | *.userprefs 58 | 59 | # Mono auto generated files 60 | mono_crash.* 61 | 62 | # Build results 63 | [Dd]ebug/ 64 | [Dd]ebugPublic/ 65 | [Rr]elease/ 66 | [Rr]eleases/ 67 | x64/ 68 | x86/ 69 | [Ww][Ii][Nn]32/ 70 | [Aa][Rr][Mm]/ 71 | [Aa][Rr][Mm]64/ 72 | bld/ 73 | [Bb]in/ 74 | [Oo]bj/ 75 | [Ll]og/ 76 | [Ll]ogs/ 77 | 78 | # Visual Studio 2015/2017 cache/options directory 79 | .vs/ 80 | # Uncomment if you have tasks that create the project's static files in wwwroot 81 | #wwwroot/ 82 | 83 | # Visual Studio 2017 auto generated files 84 | Generated\ Files/ 85 | 86 | # MSTest test Results 87 | [Tt]est[Rr]esult*/ 88 | [Bb]uild[Ll]og.* 89 | 90 | # NUnit 91 | *.VisualState.xml 92 | TestResult.xml 93 | nunit-*.xml 94 | 95 | # Build Results of an ATL Project 96 | [Dd]ebugPS/ 97 | [Rr]eleasePS/ 98 | dlldata.c 99 | 100 | # Benchmark Results 101 | BenchmarkDotNet.Artifacts/ 102 | 103 | # .NET Core 104 | project.lock.json 105 | project.fragment.lock.json 106 | artifacts/ 107 | 108 | # ASP.NET Scaffolding 109 | ScaffoldingReadMe.txt 110 | 111 | # StyleCop 112 | StyleCopReport.xml 113 | 114 | # Files built by Visual Studio 115 | *_i.c 116 | *_p.c 117 | *_h.h 118 | *.ilk 119 | *.obj 120 | *.iobj 121 | *.pch 122 | *.pdb 123 | *.ipdb 124 | *.pgc 125 | *.pgd 126 | *.rsp 127 | *.sbr 128 | *.tlb 129 | *.tli 130 | *.tlh 131 | *.tmp 132 | *.tmp_proj 133 | *_wpftmp.csproj 134 | *.log 135 | *.vspscc 136 | *.vssscc 137 | .builds 138 | *.pidb 139 | *.svclog 140 | *.scc 141 | 142 | # Chutzpah Test files 143 | _Chutzpah* 144 | 145 | # Visual C++ cache files 146 | ipch/ 147 | *.aps 148 | *.ncb 149 | *.opendb 150 | *.opensdf 151 | *.sdf 152 | *.cachefile 153 | *.VC.db 154 | *.VC.VC.opendb 155 | 156 | # Visual Studio profiler 157 | *.psess 158 | *.vsp 159 | *.vspx 160 | *.sap 161 | 162 | # Visual Studio Trace Files 163 | *.e2e 164 | 165 | # TFS 2012 Local Workspace 166 | $tf/ 167 | 168 | # Guidance Automation Toolkit 169 | *.gpState 170 | 171 | # ReSharper is a .NET coding add-in 172 | _ReSharper*/ 173 | *.[Rr]e[Ss]harper 174 | *.DotSettings.user 175 | 176 | # TeamCity is a build add-in 177 | _TeamCity* 178 | 179 | # DotCover is a Code Coverage Tool 180 | *.dotCover 181 | 182 | # AxoCover is a Code Coverage Tool 183 | .axoCover/* 184 | !.axoCover/settings.json 185 | 186 | # Coverlet is a free, cross platform Code Coverage Tool 187 | coverage*.json 188 | coverage*.xml 189 | coverage*.info 190 | 191 | # Visual Studio code coverage results 192 | *.coverage 193 | *.coveragexml 194 | 195 | # NCrunch 196 | _NCrunch_* 197 | .*crunch*.local.xml 198 | nCrunchTemp_* 199 | 200 | # MightyMoose 201 | *.mm.* 202 | AutoTest.Net/ 203 | 204 | # Web workbench (sass) 205 | .sass-cache/ 206 | 207 | # Installshield output folder 208 | [Ee]xpress/ 209 | 210 | # DocProject is a documentation generator add-in 211 | DocProject/buildhelp/ 212 | DocProject/Help/*.HxT 213 | DocProject/Help/*.HxC 214 | DocProject/Help/*.hhc 215 | DocProject/Help/*.hhk 216 | DocProject/Help/*.hhp 217 | DocProject/Help/Html2 218 | DocProject/Help/html 219 | 220 | # Click-Once directory 221 | publish/ 222 | 223 | # Publish Web Output 224 | *.[Pp]ublish.xml 225 | *.azurePubxml 226 | # Note: Comment the next line if you want to checkin your web deploy settings, 227 | # but database connection strings (with potential passwords) will be unencrypted 228 | *.pubxml 229 | *.publishproj 230 | 231 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 232 | # checkin your Azure Web App publish settings, but sensitive information contained 233 | # in these scripts will be unencrypted 234 | PublishScripts/ 235 | 236 | # NuGet Packages 237 | *.nupkg 238 | # NuGet Symbol Packages 239 | *.snupkg 240 | # The packages folder can be ignored because of Package Restore 241 | **/[Pp]ackages/* 242 | # except build/, which is used as an MSBuild target. 243 | !**/[Pp]ackages/build/ 244 | # Uncomment if necessary however generally it will be regenerated when needed 245 | #!**/[Pp]ackages/repositories.config 246 | # NuGet v3's project.json files produces more ignorable files 247 | *.nuget.props 248 | *.nuget.targets 249 | 250 | # Microsoft Azure Build Output 251 | csx/ 252 | *.build.csdef 253 | 254 | # Microsoft Azure Emulator 255 | ecf/ 256 | rcf/ 257 | 258 | # Windows Store app package directories and files 259 | AppPackages/ 260 | BundleArtifacts/ 261 | Package.StoreAssociation.xml 262 | _pkginfo.txt 263 | *.appx 264 | *.appxbundle 265 | *.appxupload 266 | 267 | # Visual Studio cache files 268 | # files ending in .cache can be ignored 269 | *.[Cc]ache 270 | # but keep track of directories ending in .cache 271 | !?*.[Cc]ache/ 272 | 273 | # Others 274 | ClientBin/ 275 | ~$* 276 | *~ 277 | *.dbmdl 278 | *.dbproj.schemaview 279 | *.jfm 280 | *.pfx 281 | *.publishsettings 282 | orleans.codegen.cs 283 | 284 | # Including strong name files can present a security risk 285 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 286 | #*.snk 287 | 288 | # Since there are multiple workflows, uncomment next line to ignore bower_components 289 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 290 | #bower_components/ 291 | 292 | # RIA/Silverlight projects 293 | Generated_Code/ 294 | 295 | # Backup & report files from converting an old project file 296 | # to a newer Visual Studio version. Backup files are not needed, 297 | # because we have git ;-) 298 | _UpgradeReport_Files/ 299 | Backup*/ 300 | UpgradeLog*.XML 301 | UpgradeLog*.htm 302 | ServiceFabricBackup/ 303 | *.rptproj.bak 304 | 305 | # SQL Server files 306 | *.mdf 307 | *.ldf 308 | *.ndf 309 | 310 | # Business Intelligence projects 311 | *.rdl.data 312 | *.bim.layout 313 | *.bim_*.settings 314 | *.rptproj.rsuser 315 | *- [Bb]ackup.rdl 316 | *- [Bb]ackup ([0-9]).rdl 317 | *- [Bb]ackup ([0-9][0-9]).rdl 318 | 319 | # Microsoft Fakes 320 | FakesAssemblies/ 321 | 322 | # GhostDoc plugin setting file 323 | *.GhostDoc.xml 324 | 325 | # Node.js Tools for Visual Studio 326 | .ntvs_analysis.dat 327 | node_modules/ 328 | 329 | # Visual Studio 6 build log 330 | *.plg 331 | 332 | # Visual Studio 6 workspace options file 333 | *.opt 334 | 335 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 336 | *.vbw 337 | 338 | # Visual Studio LightSwitch build output 339 | **/*.HTMLClient/GeneratedArtifacts 340 | **/*.DesktopClient/GeneratedArtifacts 341 | **/*.DesktopClient/ModelManifest.xml 342 | **/*.Server/GeneratedArtifacts 343 | **/*.Server/ModelManifest.xml 344 | _Pvt_Extensions 345 | 346 | # Paket dependency manager 347 | .paket/paket.exe 348 | paket-files/ 349 | 350 | # FAKE - F# Make 351 | .fake/ 352 | 353 | # CodeRush personal settings 354 | .cr/personal 355 | 356 | # Python Tools for Visual Studio (PTVS) 357 | __pycache__/ 358 | *.pyc 359 | 360 | # Cake - Uncomment if you are using it 361 | # tools/** 362 | # !tools/packages.config 363 | 364 | # Tabs Studio 365 | *.tss 366 | 367 | # Telerik's JustMock configuration file 368 | *.jmconfig 369 | 370 | # BizTalk build output 371 | *.btp.cs 372 | *.btm.cs 373 | *.odx.cs 374 | *.xsd.cs 375 | 376 | # OpenCover UI analysis results 377 | OpenCover/ 378 | 379 | # Azure Stream Analytics local run output 380 | ASALocalRun/ 381 | 382 | # MSBuild Binary and Structured Log 383 | *.binlog 384 | 385 | # NVidia Nsight GPU debugger configuration file 386 | *.nvuser 387 | 388 | # MFractors (Xamarin productivity tool) working folder 389 | .mfractor/ 390 | 391 | # Local History for Visual Studio 392 | .localhistory/ 393 | 394 | # BeatPulse healthcheck temp database 395 | healthchecksdb 396 | 397 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 398 | MigrationBackup/ 399 | 400 | # Ionide (cross platform F# VS Code tools) working folder 401 | .ionide/ 402 | 403 | # Fody - auto-generated XML schema 404 | FodyWeavers.xsd 405 | .idea/.idea.LazyECS/.idea/aws.xml 406 | .idea/.idea.LazyECS/.idea/codeStyles/codeStyleConfig.xml 407 | .idea/.idea.LazyECS/.idea/contentModel.xml 408 | .idea/.idea.LazyECS/.idea/encodings.xml 409 | .idea/.idea.LazyECS/.idea/indexLayout.xml 410 | .idea/.idea.LazyECS/.idea/modules.xml 411 | .idea/.idea.LazyECS/.idea/projectSettingsUpdater.xml 412 | .idea/.idea.LazyECS/.idea/vcs.xml 413 | .idea/.idea.LazyECS/.idea/workspace.xml 414 | .idea/.idea.LazyECS/riderModule.iml 415 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65d80a6162f880949af811b779575f5a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Unity.LazyECS.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Unity.", 3 | "references": [ 4 | "GUID:f829098a39adf5641a5db89608bd75ee" 5 | ], 6 | "includePlatforms": [ 7 | "Editor" 8 | ], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [], 16 | "noEngineReferences": false 17 | } -------------------------------------------------------------------------------- /Editor/Unity.LazyECS.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f93cf43887a5f44986d5688860fe388 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/WorldDebugger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using LazyECS; 5 | using LazyECS.Component; 6 | using LazyECS.Entity; 7 | using UnityEditor; 8 | using UnityEngine; 9 | 10 | [Serializable] 11 | class WorldDebugger : EditorWindow 12 | { 13 | [MenuItem("Tools/World Debugger")] 14 | public static void Init() 15 | { 16 | WorldDebugger window = (WorldDebugger) GetWindow(typeof(WorldDebugger), false); 17 | window.Show(); 18 | } 19 | 20 | private void Update() 21 | { 22 | if (!EditorApplication.isPlaying) return; 23 | Repaint(); 24 | } 25 | 26 | private void OnEnable() 27 | { 28 | string[] allAssetPaths = AssetDatabase.GetAllAssetPaths(); 29 | string fileName = "lazyecsworlddebugger.png"; 30 | 31 | string fullPath = ""; 32 | 33 | for (int i = 0; i < allAssetPaths.Length; ++i) 34 | { 35 | if (allAssetPaths[i].EndsWith(fileName)) 36 | { 37 | fullPath = allAssetPaths[i]; 38 | } 39 | } 40 | 41 | Texture icon = AssetDatabase.LoadAssetAtPath(fullPath); 42 | 43 | titleContent = new GUIContent("World Debugger", 44 | icon, 45 | "Debug a LazyECS world."); 46 | } 47 | 48 | [SerializeField] public SimulationController simulationController; 49 | private int currTab; 50 | private bool[] worldsFoldoutsState; 51 | private bool[] groupsFoldoutsState; 52 | private bool[] entitiesFoldoutsState; 53 | 54 | private Vector2 scrollPos; 55 | 56 | private void OnGUI() 57 | { 58 | if (!EditorApplication.isPlaying) 59 | { 60 | GUILayout.BeginHorizontal(); 61 | GUILayout.FlexibleSpace(); 62 | GUILayout.Label("Enter PlayMode", EditorStyles.boldLabel); 63 | GUILayout.FlexibleSpace(); 64 | GUILayout.EndHorizontal(); 65 | 66 | return; 67 | } 68 | 69 | if (simulationController == null) 70 | { 71 | simulationController = FindObjectOfType(); 72 | 73 | if (simulationController == null) 74 | { 75 | GUILayout.BeginHorizontal(); 76 | GUILayout.FlexibleSpace(); 77 | GUILayout.Label("Initialize Simulation Controller", EditorStyles.boldLabel); 78 | GUILayout.FlexibleSpace(); 79 | GUILayout.EndHorizontal(); 80 | 81 | return; 82 | } 83 | } 84 | 85 | if (worldsFoldoutsState == null || worldsFoldoutsState.Length != simulationController.Worlds.Count) 86 | { 87 | worldsFoldoutsState = new bool[simulationController.Worlds.Count]; 88 | groupsFoldoutsState = new bool[simulationController.Worlds.Count]; 89 | entitiesFoldoutsState = new bool[simulationController.Worlds.Count]; 90 | } 91 | 92 | if (worldsFoldoutsState.Length == 0) 93 | { 94 | GUILayout.BeginHorizontal(); 95 | GUILayout.FlexibleSpace(); 96 | GUILayout.Label("No worlds found", EditorStyles.boldLabel); 97 | GUILayout.FlexibleSpace(); 98 | GUILayout.EndHorizontal(); 99 | 100 | return; 101 | } 102 | 103 | string[] tabs = {"Worlds", "Settings"}; 104 | currTab = GUILayout.Toolbar(currTab, tabs); 105 | 106 | if (currTab == 0) 107 | { 108 | if (GUILayout.Button("Collapse", GUILayout.Width(75))) 109 | { 110 | for (var i = 0; i < worldsFoldoutsState.Length; i++) 111 | { 112 | worldsFoldoutsState[i] = false; 113 | } 114 | 115 | for (var i = 0; i < groupsFoldoutsState.Length; i++) 116 | { 117 | groupsFoldoutsState[i] = false; 118 | } 119 | 120 | for (var i = 0; i < entitiesFoldoutsState.Length; i++) 121 | { 122 | entitiesFoldoutsState[i] = false; 123 | } 124 | } 125 | 126 | int currWorldFoldout = 0; 127 | 128 | scrollPos = EditorGUILayout.BeginScrollView(scrollPos); 129 | 130 | foreach (KeyValuePair world in simulationController.Worlds) 131 | { 132 | GUIStyle worldFoldoutStyle = new GUIStyle(EditorStyles.foldout); 133 | 134 | if (world.Value.GetType().BaseType.Name == "NetworkWorld") 135 | { 136 | Color networkWorldFoldoutColor = new Color(0, 204, 255); 137 | worldFoldoutStyle.normal.textColor = networkWorldFoldoutColor; 138 | worldFoldoutStyle.onNormal.textColor = networkWorldFoldoutColor; 139 | worldFoldoutStyle.hover.textColor = networkWorldFoldoutColor; 140 | worldFoldoutStyle.onHover.textColor = networkWorldFoldoutColor; 141 | worldFoldoutStyle.focused.textColor = networkWorldFoldoutColor; 142 | worldFoldoutStyle.onFocused.textColor = networkWorldFoldoutColor; 143 | worldFoldoutStyle.active.textColor = networkWorldFoldoutColor; 144 | worldFoldoutStyle.onActive.textColor = networkWorldFoldoutColor; 145 | } 146 | 147 | string worldFoldoutName = world.Value.GetType().Name 148 | + "(ID: " 149 | + world.Key 150 | + ", Groups: " 151 | + world.Value.Groups.Count 152 | + ", Entities: " 153 | + world.Value.Entities.Count 154 | + ", Features: " 155 | + world.Value.Features.Length 156 | + ")"; 157 | 158 | worldsFoldoutsState[currWorldFoldout] = EditorGUILayout.Foldout(worldsFoldoutsState[currWorldFoldout], worldFoldoutName, worldFoldoutStyle); 159 | 160 | if (worldsFoldoutsState[currWorldFoldout]) 161 | { 162 | EditorGUI.indentLevel++; 163 | groupsFoldoutsState[currWorldFoldout] = EditorGUILayout.Foldout(groupsFoldoutsState[currWorldFoldout], "Groups (" + world.Value.Groups.Count + ")"); 164 | EditorGUI.indentLevel++; 165 | 166 | if (groupsFoldoutsState[currWorldFoldout]) 167 | { 168 | int groupCount = 0; 169 | 170 | foreach (Group group in world.Value.Groups) 171 | { 172 | string label = "Group " + groupCount + " entities: " + group.Entities.Count + ", filters (" + group.Filters.Count + ") : "; 173 | 174 | int compCount = 0; 175 | foreach (Type filter in group.Filters) 176 | { 177 | label += filter.Name; 178 | 179 | if (compCount < group.Filters.Count - 1) 180 | label += ", "; 181 | 182 | compCount++; 183 | } 184 | 185 | EditorGUILayout.LabelField(label); 186 | groupCount++; 187 | } 188 | } 189 | 190 | EditorGUI.indentLevel--; 191 | entitiesFoldoutsState[currWorldFoldout] = EditorGUILayout.Foldout(entitiesFoldoutsState[currWorldFoldout], "Entities (" + world.Value.Entities.Count + ")"); 192 | EditorGUI.indentLevel++; 193 | 194 | if (entitiesFoldoutsState[currWorldFoldout]) 195 | { 196 | foreach (KeyValuePair entity in world.Value.Entities) 197 | { 198 | string label = "Entity " + entity.Key + " ("; 199 | 200 | 201 | int compCount = 0; 202 | foreach (KeyValuePair component in entity.Value.Components) 203 | { 204 | label += component.Key.Name; 205 | 206 | if (compCount < entity.Value.Components.Count - 1) 207 | label += ", "; 208 | 209 | compCount++; 210 | } 211 | 212 | label += ")"; 213 | 214 | EditorGUILayout.Foldout(true, label); 215 | 216 | EditorGUI.indentLevel++; 217 | 218 | foreach (KeyValuePair component in entity.Value.Components) 219 | { 220 | GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout); 221 | 222 | if (component.Key.GetInterface("INetworkComponent") != null) 223 | { 224 | Color networkComponentColor = new Color(0, 204, 255); 225 | foldoutStyle.normal.textColor = networkComponentColor; 226 | foldoutStyle.onNormal.textColor = networkComponentColor; 227 | foldoutStyle.hover.textColor = networkComponentColor; 228 | foldoutStyle.onHover.textColor = networkComponentColor; 229 | foldoutStyle.focused.textColor = networkComponentColor; 230 | foldoutStyle.onFocused.textColor = networkComponentColor; 231 | foldoutStyle.active.textColor = networkComponentColor; 232 | foldoutStyle.onActive.textColor = networkComponentColor; 233 | } 234 | 235 | string componentValue; 236 | 237 | if (component.Value.Get() == null) 238 | componentValue = "NULL"; 239 | else 240 | componentValue = component.Value.Get().GetType().Name; 241 | 242 | string componentName; 243 | 244 | if (component.Key == null) 245 | componentName = "NULL"; 246 | else 247 | componentName = component.Key.Name; 248 | 249 | 250 | EditorGUILayout.Foldout(true, componentName + " (" + componentValue + ")", foldoutStyle); 251 | EditorGUI.indentLevel++; 252 | 253 | if (component.Value.Get() == null) continue; 254 | 255 | switch (component.Value.Get().GetType().Name) 256 | { 257 | case "UInt16": 258 | case "UInt32": 259 | EditorGUILayout.SelectableLabel(((uint) component.Value.Get()).ToString(), GUILayout.Height(15)); 260 | break; 261 | case "Int16": 262 | case "Int32": 263 | EditorGUILayout.SelectableLabel(((int) component.Value.Get()).ToString(), GUILayout.Height(15)); 264 | break; 265 | case "Single": 266 | case "Double": 267 | case "Long": 268 | EditorGUILayout.SelectableLabel(((float) component.Value.Get()).ToString(CultureInfo.InvariantCulture), GUILayout.Height(15)); 269 | break; 270 | case "Boolean": 271 | EditorGUILayout.Toggle((bool) component.Value.Get()); 272 | break; 273 | case "String": 274 | EditorGUILayout.SelectableLabel((string) component.Value.Get(), GUILayout.Height(15)); 275 | break; 276 | case "Vector3": 277 | Vector3 vector3 = (Vector3) component.Value.Get(); 278 | EditorGUILayout.SelectableLabel($"x: {vector3.x}, y: {vector3.y}, z: {vector3.z}", GUILayout.Height(15)); 279 | break; 280 | case "Vector2": 281 | Vector2 vector2 = (Vector2) component.Value.Get(); 282 | EditorGUILayout.SelectableLabel($"x: {vector2.x}, y: {vector2.y}", GUILayout.Height(15)); 283 | break; 284 | case "GameObject": 285 | EditorGUILayout.ObjectField((GameObject) component.Value.Get(), typeof(GameObject), true); 286 | break; 287 | case "String[]": 288 | EditorGUILayout.SelectableLabel(CreateStringArrayDisplayLabel((string[])component.Value.Get()), GUILayout.Height(15)); 289 | break; 290 | default: 291 | EditorGUILayout.SelectableLabel(component.Value.Get().GetType().Name + " (Custom editor needed to display value)", GUILayout.Height(15)); 292 | break; 293 | } 294 | 295 | EditorGUI.indentLevel--; 296 | } 297 | 298 | EditorGUI.indentLevel--; 299 | } 300 | } 301 | } 302 | 303 | EditorGUI.indentLevel = 0; 304 | currWorldFoldout++; 305 | } 306 | 307 | EditorGUILayout.EndScrollView(); 308 | } 309 | } 310 | 311 | private string CreateStringArrayDisplayLabel(string[] data) 312 | { 313 | string labelValue = ""; 314 | 315 | for (var i = 0; i < data.Length; i++) 316 | { 317 | labelValue += i + ": " + data[i]; 318 | 319 | if (i != data.Length - 1) 320 | labelValue += ", "; 321 | } 322 | 323 | return labelValue; 324 | } 325 | } -------------------------------------------------------------------------------- /Editor/WorldDebugger.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76acf36bff11a1845bcc974ded3bb289 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Icon.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bb133be1ef40302438f7f6d85ce1dd2a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Icon/lazyecsworlddebugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatelax/LazyECS/5633601082863997c899d689fb232f7bd10e7642/Icon/lazyecsworlddebugger.png -------------------------------------------------------------------------------- /Icon/lazyecsworlddebugger.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9b95143b77f4bb44b26108b2e45f001 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 0 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: 1 36 | mipBias: -100 37 | wrapU: 1 38 | wrapV: 1 39 | wrapW: -1 40 | nPOTScale: 0 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 1 53 | spriteTessellationDetail: -1 54 | textureType: 2 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 32 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | - serializedVersion: 3 75 | buildTarget: Standalone 76 | maxTextureSize: 32 77 | resizeAlgorithm: 0 78 | textureFormat: -1 79 | textureCompression: 1 80 | compressionQuality: 50 81 | crunchedCompression: 0 82 | allowsAlphaSplitting: 0 83 | overridden: 0 84 | androidETC2FallbackOverride: 0 85 | forceMaximumCompressionQuality_BC6H_BC7: 0 86 | - serializedVersion: 3 87 | buildTarget: Windows Store Apps 88 | maxTextureSize: 32 89 | resizeAlgorithm: 0 90 | textureFormat: -1 91 | textureCompression: 1 92 | compressionQuality: 50 93 | crunchedCompression: 0 94 | allowsAlphaSplitting: 0 95 | overridden: 0 96 | androidETC2FallbackOverride: 0 97 | forceMaximumCompressionQuality_BC6H_BC7: 0 98 | - serializedVersion: 3 99 | buildTarget: iPhone 100 | maxTextureSize: 32 101 | resizeAlgorithm: 0 102 | textureFormat: -1 103 | textureCompression: 1 104 | compressionQuality: 50 105 | crunchedCompression: 0 106 | allowsAlphaSplitting: 0 107 | overridden: 0 108 | androidETC2FallbackOverride: 0 109 | forceMaximumCompressionQuality_BC6H_BC7: 0 110 | spriteSheet: 111 | serializedVersion: 2 112 | sprites: [] 113 | outline: [] 114 | physicsShape: [] 115 | bones: [] 116 | spriteID: 117 | internalID: 0 118 | vertices: [] 119 | indices: 120 | edges: [] 121 | weights: [] 122 | secondaryTextures: [] 123 | spritePackingTag: 124 | pSDRemoveMatte: 0 125 | pSDShowRemoveMatteOption: 0 126 | userData: 127 | assetBundleName: 128 | assetBundleVariant: 129 | -------------------------------------------------------------------------------- /Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c763b639b4aef4c419761e4be4835d53 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Images/worlddebugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatelax/LazyECS/5633601082863997c899d689fb232f7bd10e7642/Images/worlddebugger.png -------------------------------------------------------------------------------- /Images/worlddebugger.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76fe4f72ef415b14a8fe0c9ccfe468d2 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tate McCormick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33b34f6feefca824fa4a62aca978adc7 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | LazyECS is an ECS framework designed to be quick to learn and implement while still remaining performant and scalable. 4 | 5 | # Features 6 | 7 | * No codegen 8 | * Simple 9 | * Easy to learn (faster onboarding) 10 | * No Unity dependency (Excluding Debug.Log used in the source code and [WorldDebugger](https://github.com/tatelax/LazyECS/wiki/World-Debugger)) 11 | 12 | # How to Install 13 | LazyECS can be installed via the Unity Package Manager or by downloading from GitHub and importing into your project. 14 | ``` 15 | Window/Package Manager => + Button => "Add package from git URL..." => https://github.com/tatelax/LazyECS.git 16 | ``` 17 | 18 | # Usage 19 | 20 | See [Wiki](https://github.com/tatelax/LazyECS/wiki/) 21 | 22 | # Addons 23 | 24 | ### [LazyECS.Networking](https://github.com/tatelax/LazyECS.Networking) 25 | This addon provides the ability to synchronize entities and their component data across the networking using [Mirror](https://github.com/vis2k/Mirror) 26 | 27 | # Example Project 28 | 29 | [Here's an example project in Unity 2019.](https://github.com/tatelax/LazyECSExample) 30 | 31 | # Known Issues 32 | None 33 | 34 | # To-Do 35 | * Unit tests 36 | * Multi-threading 37 | * Replace classes with structs where possible 38 | 39 | # Important Info 40 | 41 | Lazy ECS is not production ready. There might be bugs. 42 | 43 | # Support 44 | 45 | You can contact me on Discord: ```tatelax#0001``` 46 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9bfbda4676072f4d99913388fd2745d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a2c5b4b0b5f5a84cbb51b0e206abf84 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/ComponentLookup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using LazyECS.Component; 6 | using UnityEngine; 7 | 8 | public static class ComponentLookup 9 | { 10 | private static List Components { get; set; } 11 | 12 | public static void Init() 13 | { 14 | Components = new List(); 15 | 16 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) 17 | { 18 | foreach (Type type in assembly.GetTypes()) 19 | { 20 | if (type.IsInterface || !type.GetInterfaces().Contains(typeof(IComponent))) continue; 21 | 22 | Components.Add(type); 23 | } 24 | } 25 | } 26 | 27 | public static Type Get(int id) 28 | { 29 | return Components[id]; 30 | } 31 | 32 | public static int Get(Type type) 33 | { 34 | //TODO: Maybe if Components was a Dictionary this could be faster 35 | for (int i = 0; i < Components.Count; i++) 36 | { 37 | if (Components[i] == type) 38 | { 39 | return i; 40 | } 41 | } 42 | 43 | Debug.LogError($"Unable to find component {type.Name}"); 44 | return -1; 45 | } 46 | } -------------------------------------------------------------------------------- /Runtime/ComponentLookup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 311a1d7f45af4057b55ceaf92a7bae39 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using LazyECS.Component; 4 | using UnityEngine; 5 | 6 | namespace LazyECS.Entity 7 | { 8 | public delegate void ComponentAdded(Entity entity, IComponent component); 9 | public delegate void ComponentRemoved(Entity entity, IComponent component); 10 | public delegate void ComponentSet(Entity entity, IComponent component, bool setFromNetworkMessage); 11 | 12 | public class Entity : IEntity, IDisposable 13 | { 14 | public int id { get; } 15 | public Dictionary Components { get; } 16 | 17 | public event ComponentAdded OnComponentAdded; 18 | public event ComponentRemoved OnComponentRemoved; 19 | public event ComponentSet OnComponentSet; 20 | 21 | public Entity(int _id = default) 22 | { 23 | //TODO: ID collision checking (proper id generation) 24 | id = _id; 25 | 26 | Components = new Dictionary(); 27 | } 28 | 29 | public void Dispose() 30 | { 31 | OnComponentAdded = null; 32 | OnComponentRemoved = null; 33 | OnComponentSet = null; 34 | } 35 | 36 | /// 37 | /// Add component by id 38 | /// 39 | /// 40 | public IComponent Add(int componentId) 41 | { 42 | IComponent component = (IComponent)Activator.CreateInstance(ComponentLookup.Get(componentId)); 43 | Components.Add(component.GetType(), component); 44 | 45 | OnComponentAdded?.Invoke(this, component); 46 | return component; 47 | } 48 | 49 | /// 50 | /// Add component by type 51 | /// 52 | /// Component type 53 | /// 54 | public TComponent Add() where TComponent : IComponent, new() 55 | { 56 | TComponent component = new TComponent(); 57 | Components.Add(component.GetType(), component); 58 | 59 | OnComponentAdded?.Invoke(this, component); 60 | return component; 61 | } 62 | 63 | public IComponent Get(int id) 64 | { 65 | Type compType = ComponentLookup.Get(id); 66 | if (!Components.ContainsKey(compType)) 67 | { 68 | Debug.LogWarning($"Tried to access component {compType} but the entity didn't have it!"); 69 | return default; 70 | } 71 | 72 | return Components[compType]; 73 | } 74 | 75 | public IComponent Get(Type type) 76 | { 77 | if (!Components.ContainsKey(type)) 78 | { 79 | Debug.LogWarning($"Tried to access component {type} but the entity didn't have it!"); 80 | return default; 81 | } 82 | 83 | return Components[type]; 84 | } 85 | 86 | public TComponent Get() where TComponent : IComponent 87 | { 88 | Type compType = typeof(TComponent); 89 | if (!Components.ContainsKey(compType)) 90 | { 91 | Debug.LogWarning($"Tried to access component {compType} but the entity didn't have it!"); 92 | return default; 93 | } 94 | 95 | return (TComponent) Components[compType]; 96 | } 97 | 98 | /// 99 | /// Check if entity has a component by type (param) 100 | /// 101 | /// 102 | /// 103 | internal bool Has(Type type) 104 | { 105 | return Components.ContainsKey(type); 106 | } 107 | 108 | /// 109 | /// Check if entity has a component by id 110 | /// 111 | /// 112 | /// 113 | public bool Has(int id) 114 | { 115 | return Components.ContainsKey(ComponentLookup.Get(id)); 116 | } 117 | 118 | /// 119 | /// Check if entity has a component by type 120 | /// 121 | /// 122 | /// 123 | public bool Has() where TComponent : IComponent 124 | { 125 | return Components.ContainsKey(typeof(TComponent)); 126 | } 127 | 128 | /// 129 | /// Remove component by id 130 | /// 131 | /// 132 | public void Remove(int componentId) 133 | { 134 | Type component = ComponentLookup.Get(componentId); 135 | 136 | if (!Components.ContainsKey(component)) 137 | { 138 | Debug.LogWarning($"Tried to remove component {componentId} but the entity didn't have it!"); 139 | return; 140 | } 141 | 142 | IComponent cachedComponentBeforeRemoval = Components[component]; 143 | Components.Remove(component); 144 | OnComponentRemoved?.Invoke( this, cachedComponentBeforeRemoval); 145 | } 146 | 147 | public void Remove() where TComponent : IComponent 148 | { 149 | IComponent component = Components[typeof(TComponent)]; 150 | Components.Remove(component.GetType()); 151 | OnComponentRemoved?.Invoke(this, component); 152 | } 153 | 154 | public void Replace() where TComponent : IComponent, new() 155 | { 156 | Remove(); 157 | Add(); 158 | } 159 | 160 | public void Set(object componentValue = null, bool setFromNetworkMessage = false) where TComponent : IComponent, new() 161 | { 162 | Type compType = typeof(TComponent); 163 | 164 | if (!Components.TryGetValue(compType, out IComponent component)) 165 | { 166 | component = Add(); 167 | } 168 | 169 | component.Set(componentValue); 170 | 171 | OnComponentSet?.Invoke(this, component, setFromNetworkMessage); 172 | } 173 | 174 | public void Set(int id, object componentValue = null, bool setFromNetworkMessage = false) 175 | { 176 | Type compType = ComponentLookup.Get(id); 177 | 178 | if (!Components.TryGetValue(compType, out IComponent component)) 179 | { 180 | component = Add(id); 181 | } 182 | 183 | component.Set(componentValue); 184 | 185 | OnComponentSet?.Invoke(this, component, setFromNetworkMessage); 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /Runtime/Entity.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b289fd389d1d7a1439726524125a655f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Feature.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public abstract class Feature : IFeature 4 | { 5 | public Systems Systems { get; protected set; } 6 | 7 | public void Initialize() 8 | { 9 | Systems?.Initialize(); 10 | } 11 | 12 | public void Update() 13 | { 14 | Systems?.Update(); 15 | } 16 | 17 | public void Teardown() 18 | { 19 | Systems?.Teardown(); 20 | } 21 | 22 | public void Cleanup() 23 | { 24 | Systems?.Cleanup(); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Runtime/Feature.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4dc5e2ea4079164448da45e00121e5c8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Group.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace LazyECS 7 | { 8 | public class Group : IGroup, IDisposable 9 | { 10 | public delegate void OnEntityUpdate(EventType _eventType, Entity.Entity entity, Type component); 11 | 12 | public HashSet Entities { get; } 13 | public HashSet Filters { get; } // We have to use Type because we aren't storing instances of IComponents, only their type 14 | public GroupType GroupType { get; } 15 | public EventType EventType { get; } 16 | 17 | public event OnEntityUpdate OnEntityUpdateEvent; 18 | 19 | public Group(GroupType groupType, HashSet filters, EventType _eventType, OnEntityUpdate onEntityUpdate) 20 | { 21 | Filters = filters; 22 | EventType = _eventType; 23 | OnEntityUpdateEvent += onEntityUpdate; 24 | Entities = new HashSet(); 25 | GroupType = groupType; 26 | } 27 | 28 | public void Dispose() 29 | { 30 | OnEntityUpdateEvent = null; 31 | } 32 | 33 | public void EntitySet(Entity.Entity entity, Type component) 34 | { 35 | if (Entities.Contains(entity) && Filters.Contains(component)) 36 | { 37 | if(EventType == EventType.Set || EventType == EventType.All) 38 | OnEntityUpdateEvent?.Invoke(EventType.Set, entity, component); 39 | } 40 | } 41 | 42 | public void EntityDestroyed(Entity.Entity entity) 43 | { 44 | if (Entities.Contains(entity)) 45 | Entities.Remove(entity); 46 | } 47 | 48 | public void ComponentAddedToEntity(Entity.Entity entity, Type component) 49 | { 50 | if (Entities.Contains(entity)) return; 51 | 52 | if (GroupType == GroupType.Any && Filters.Contains(component)) 53 | { 54 | Entities.Add(entity); 55 | 56 | if(EventType == EventType.Added || EventType == EventType.All) 57 | OnEntityUpdateEvent?.Invoke(EventType.Added, entity, component); 58 | 59 | return; 60 | } 61 | 62 | int matches = entity.Components.Sum(cmp => Filters.Count(filter => cmp.Key == filter)); 63 | 64 | if (matches != Filters.Count) return; 65 | 66 | Entities.Add(entity); 67 | 68 | if(EventType == EventType.Added || EventType == EventType.All) 69 | OnEntityUpdateEvent?.Invoke(EventType.Added, entity, component); 70 | } 71 | 72 | public void ComponentRemovedFromEntity(Entity.Entity entity, Type component) 73 | { 74 | if (GroupType == GroupType.All && Filters.Contains(component)) 75 | { 76 | Entities.Remove(entity); 77 | 78 | if(EventType == EventType.Removed || EventType == EventType.All) 79 | OnEntityUpdateEvent?.Invoke(EventType.Removed, entity, component); 80 | 81 | return; 82 | } 83 | 84 | if (entity.Components.Any(cmp => Filters.Contains(cmp.Key))) 85 | return; 86 | 87 | Entities.Remove(entity); 88 | 89 | if(EventType == EventType.Removed || EventType == EventType.All) 90 | OnEntityUpdateEvent?.Invoke(EventType.Removed, entity, component); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /Runtime/Group.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04a025a7ceaf2ae4b95e1784e5d00326 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ICleanupSystem.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public interface ICleanupSystem : ISystem 4 | { 5 | void Cleanup(); 6 | } 7 | } -------------------------------------------------------------------------------- /Runtime/ICleanupSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0019b40dd88261643bd53f6f44b7d6a4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IComponent.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS.Component 2 | { 3 | public interface IComponent 4 | { 5 | bool Set(object value = null); 6 | object Get(); 7 | } 8 | } -------------------------------------------------------------------------------- /Runtime/IComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2038e26b6230294088a291a3a876522 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using LazyECS.Component; 4 | 5 | namespace LazyECS.Entity 6 | { 7 | internal interface IEntity 8 | { 9 | int id { get; } 10 | Dictionary Components { get; } 11 | 12 | event ComponentAdded OnComponentAdded; 13 | event ComponentRemoved OnComponentRemoved; 14 | event ComponentSet OnComponentSet; 15 | 16 | IComponent Add(int componentId); 17 | TComponent Add() where TComponent : IComponent, new(); 18 | IComponent Get(int id); 19 | IComponent Get(Type type); 20 | TComponent Get() where TComponent : IComponent; 21 | bool Has(int id); 22 | bool Has() where TComponent : IComponent; 23 | void Remove(int componentId); 24 | void Remove() where TComponent : IComponent; 25 | void Replace() where TComponent : IComponent, new(); 26 | void Set(object value, bool setFromNetworkMessage = false) where TComponent : IComponent, new(); 27 | void Set(int id, object value, bool setFromNetworkMessage = false); 28 | } 29 | } -------------------------------------------------------------------------------- /Runtime/IEntity.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36a828a0e886f90408fab208fecacece 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IEventSystem.cs: -------------------------------------------------------------------------------- 1 | using LazyECS; 2 | 3 | public interface IEventSystem : ISystem { } -------------------------------------------------------------------------------- /Runtime/IEventSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d62416deb14604a4c9b6a5099b77f5bb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IFeature.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | internal interface IFeature 4 | { 5 | void Initialize(); 6 | void Update(); 7 | void Teardown(); 8 | void Cleanup(); 9 | Systems Systems { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /Runtime/IFeature.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ca0b9a9c597a3134abf13e215a1ed88d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LazyECS 5 | { 6 | public enum GroupType 7 | { 8 | Any, 9 | All 10 | } 11 | 12 | public enum EventType 13 | { 14 | Added, 15 | Set, 16 | Removed, 17 | All 18 | } 19 | 20 | public interface IGroup 21 | { 22 | GroupType GroupType { get; } 23 | HashSet Entities { get; } 24 | HashSet Filters { get; } 25 | EventType EventType { get; } 26 | 27 | event Group.OnEntityUpdate OnEntityUpdateEvent; 28 | 29 | void EntitySet(Entity.Entity entity, Type component); 30 | void EntityDestroyed(Entity.Entity entity); 31 | void ComponentAddedToEntity(Entity.Entity entity, Type component); 32 | void ComponentRemovedFromEntity(Entity.Entity entity, Type component); 33 | } 34 | } -------------------------------------------------------------------------------- /Runtime/IGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 72cb8e47af2549747999b529daa0db7b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IInitializeSystem.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public interface IInitializeSystem : ISystem 4 | { 5 | void Initialize(); 6 | } 7 | } -------------------------------------------------------------------------------- /Runtime/IInitializeSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8136815f503741045a1242edc6a7791d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ISystem.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public interface ISystem 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /Runtime/ISystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e795b17315c609541bdea1b8b1b547d3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/ITeardownSystem.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public interface ITeardownSystem : ISystem 4 | { 5 | void Teardown(); 6 | } 7 | } -------------------------------------------------------------------------------- /Runtime/ITeardownSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af3b446a31664024d89eb13ae8d37bc8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IUpdateSystem.cs: -------------------------------------------------------------------------------- 1 | namespace LazyECS 2 | { 3 | public interface IUpdateSystem : ISystem 4 | { 5 | void Update(); 6 | } 7 | } -------------------------------------------------------------------------------- /Runtime/IUpdateSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 439be934f0a3be6439542121baefcdd7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IWorld.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using LazyECS.Component; 4 | 5 | namespace LazyECS 6 | { 7 | public interface IWorld 8 | { 9 | Feature[] Features { get; } 10 | Dictionary Entities { get; } 11 | List Groups { get; } 12 | 13 | event World.EntityCreatedEvent OnEntityCreatedEvent; 14 | event World.EntityDestroyedEvent OnEntityDestroyedEvent; 15 | event World.ComponentAddedToEntity OnComponentAddedToEntityEvent; 16 | event World.ComponentRemovedFromEntity OnComponentRemovedFromEntityEvent; 17 | event World.ComponentSetOnEntity OnComponentSetOnEntityEvent; 18 | 19 | Entity.Entity CreateEntity(int id = -1, bool entityCreatedFromNetworkMessage = false); 20 | bool DestroyEntity(int id, bool entityDestroyedFromNetworkMessage = false); 21 | bool DestroyEntity(Entity.Entity entity, bool entityDestroyedFromNetworkMessage = false); 22 | void DestroyAllEntities(); 23 | 24 | void Initialize(); 25 | void Start(); 26 | void Update(); 27 | void Teardown(); 28 | void Cleanup(); 29 | 30 | Group CreateGroup(GroupType groupType, HashSet filters, EventType _eventType, Group.OnEntityUpdate OnEntityUpdate, bool checkExisting = true); 31 | 32 | LazyECS.Entity.Entity GetEntity(int id); 33 | List GetEntities(); 34 | List GetEntities() where TComponent : IComponent; 35 | List GetEntities(object value) where TComponent : IComponent; 36 | 37 | void OnEntityCreated(Entity.Entity entity, bool entityCreatedFromNetworkMessage); 38 | void OnEntityDestroyed(Entity.Entity entity, bool entityDestroyedFromNetworkMessage = false); 39 | void OnComponentAddedToEntity(Entity.Entity entity, IComponent component); 40 | void OnComponentRemovedFromEntity(Entity.Entity entity, IComponent component); 41 | void OnComponentSetOnEntity(Entity.Entity entity, IComponent component, bool setFromNetworkMessage); 42 | } 43 | } -------------------------------------------------------------------------------- /Runtime/IWorld.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26c4b038f0edaa04283e54a9cca6a446 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/MonoBehaviourSingleton.cs: -------------------------------------------------------------------------------- 1 | // MonoBehaviourSingleton: this is a base mechanism for singleton-style MonoBehaviour classes, used 2 | // when you want only one instance of an object. 3 | // 4 | // The singleton instance can come in as part of a loaded scene or instantiated 5 | // prefab, or can be created at runtime using GameObject's component-creation 6 | // functions (such as AddComponent()). 7 | // 8 | // Instance setup comes through the Awake() function, which we've made virtual. This was 9 | // necessary to support singletons that come in as part of a prefab, due to the fact that 10 | // the class will actually have TWO instances created (one inside the prefab resource and 11 | // one when the prefab is instantiated). Only the instantiated copy will be used in the 12 | // running game and receive Awake(). 13 | // 14 | // Also note that cleanup happens automatically when the singleton object is slated for 15 | // destruction, such as when it is removed from its owner GameObject or if that GameObject 16 | // is marked to be destroyed. This happens at the end of the Unity's Update loop for that 17 | // frame, when OnDestroy is called, without waiting for the the garbage collector to run. 18 | // 19 | // How to derive your own: 20 | // 21 | // public class MySingletonManager : MonoBehaviourSingleton 22 | // { ... } 23 | // 24 | // // If you override Awake() in your class, you must call base.Awake() inside it. 25 | // protected override void Awake() 26 | // { 27 | // base.Awake(); 28 | // ... 29 | // } 30 | // 31 | // How to check for validity: if (MySingletonManager.IsInstanceValid()) { ... do something ... } 32 | // How to access: MySingletonManager.Instance 33 | // 34 | // Originally based on http://wiki.unity3d.com/index.php?title=Singleton 35 | 36 | using UnityEngine; 37 | 38 | 39 | public class MonoBehaviourSingleton : MonoBehaviour where T : MonoBehaviour 40 | { 41 | /// 42 | /// Safely checks whether the singleton instance is set. 43 | /// 44 | public static bool IsInstanceValid() 45 | { 46 | return (_instance != null); 47 | } 48 | 49 | /// 50 | /// Get access to the singleton instance of the class. Note that this does not currently perform lazy instantiation if there is no instance set. 51 | /// 52 | public static T Instance 53 | { 54 | get 55 | { 56 | // NOTE: this null check exists so we can catch the case when the instance is tagged for destruction under-the-hood. 57 | // Unity system objects (including MonoBehaviours) have their == and != operators overloaded so that when they 58 | // are tagged for destruction, these operators return true when the reference is compared with null. So if 59 | // _instance is actually null OR tagged for destruction, we don't want anyone accessing it or calling functions 60 | // on it. 61 | if(_instance == null) 62 | return null; 63 | 64 | return _instance; 65 | } 66 | 67 | // NOTE: the 'set' is private because setting a new instance happens internally in Awake(), and nulling 68 | // happens automatically when the object is slated for destruction, by Unity. Think very carefully 69 | // whether you really need to change it or not. 70 | private set 71 | { 72 | Debug.Assert((value == null || _instance == null || ReferenceEquals(_instance, value)), "Multiple singleton-style Components of type " + typeof(T).Name + " detected. There should only be one."); 73 | _instance = value; 74 | } 75 | } 76 | 77 | private static T _instance = null; 78 | 79 | // NOTE: If you override Awake() in your class, you must call base.Awake() inside it. 80 | protected virtual void Awake() 81 | { 82 | Instance = this as T; 83 | } 84 | 85 | protected virtual void OnDestroy() 86 | { 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Runtime/MonoBehaviourSingleton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ccdea10def6811a459b22648c2eb01c8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/SimulationController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LazyECS; 5 | using UnityEngine; 6 | 7 | public enum LogLevel 8 | { 9 | None, 10 | Verbose 11 | } 12 | 13 | public class SimulationController : MonoBehaviourSingleton 14 | { 15 | [SerializeField] private LogLevel logLevel; 16 | public LogLevel LogLevel => logLevel; 17 | 18 | public Dictionary Worlds { get; private set; } 19 | public event EventHandler OnWorldsInitialized; 20 | 21 | protected override void Awake() 22 | { 23 | base.Awake(); 24 | 25 | Worlds = new Dictionary(); 26 | ComponentLookup.Init(); 27 | } 28 | 29 | private void StartWorlds() 30 | { 31 | foreach (KeyValuePair world in Worlds) 32 | { 33 | world.Value.Start(); 34 | } 35 | } 36 | 37 | protected virtual void Update() 38 | { 39 | foreach (KeyValuePair world in Worlds) 40 | { 41 | world.Value.Update(); 42 | world.Value.Cleanup(); 43 | } 44 | } 45 | 46 | protected void OnDisable() 47 | { 48 | foreach (KeyValuePair world in Worlds) 49 | { 50 | world.Value.Teardown(); 51 | } 52 | } 53 | 54 | protected void InitializeWorlds(IWorld[] worlds) 55 | { 56 | if (worlds.Length > 0) 57 | Reset(); 58 | 59 | Worlds = new Dictionary(); 60 | 61 | for (int i = 0; i < worlds.Length; i++) 62 | { 63 | Worlds.Add(i, worlds[i]); 64 | } 65 | 66 | foreach (KeyValuePair world in Worlds) 67 | { 68 | world.Value.Initialize(); 69 | } 70 | 71 | OnWorldsInitialized?.Invoke(this, EventArgs.Empty); 72 | 73 | StartWorlds(); 74 | } 75 | 76 | protected void Reset() 77 | { 78 | foreach (KeyValuePair world in Worlds) 79 | { 80 | world.Value.Teardown(); 81 | (world.Value as IDisposable).Dispose(); 82 | } 83 | 84 | Worlds = new Dictionary(); 85 | } 86 | 87 | protected void DestroyAllEntitiesInAllWorlds() 88 | { 89 | foreach (KeyValuePair world in Worlds) 90 | { 91 | world.Value.DestroyAllEntities(); 92 | } 93 | } 94 | 95 | public IWorld GetWorld(int id) 96 | { 97 | return Worlds[id]; 98 | } 99 | 100 | public IWorld GetWorld() where T : IWorld 101 | { 102 | return Worlds.First(x => x.Value.GetType() == typeof(T)).Value; 103 | } 104 | 105 | public int GetWorldId(IWorld world) 106 | { 107 | return Worlds.First(x => x.Value == world).Key; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Runtime/SimulationController.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 132a269aede60aa4eae3d944b9aa9648 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Systems.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace LazyECS 4 | { 5 | public class Systems 6 | { 7 | protected readonly List initializeSystems; 8 | protected readonly List updateSystems; 9 | protected readonly List eventSystems; 10 | protected readonly List teardownSystems; 11 | protected readonly List cleanupSystems; 12 | 13 | public Systems() 14 | { 15 | initializeSystems = new List(); 16 | updateSystems = new List(); 17 | eventSystems = new List(); 18 | teardownSystems = new List(); 19 | cleanupSystems = new List(); 20 | } 21 | 22 | public virtual Systems Add(ISystem system) 23 | { 24 | if (system is IInitializeSystem initializeSystem) 25 | initializeSystems.Add(initializeSystem); 26 | 27 | if (system is IUpdateSystem updateSystem) 28 | updateSystems.Add(updateSystem); 29 | 30 | if (system is IEventSystem eventSystem) 31 | eventSystems.Add(eventSystem); 32 | 33 | if (system is ITeardownSystem teardownSystem) 34 | teardownSystems.Add(teardownSystem); 35 | 36 | if (system is ICleanupSystem cleanupSystem) 37 | cleanupSystems.Add(cleanupSystem); 38 | 39 | return this; 40 | } 41 | 42 | public virtual void Initialize() 43 | { 44 | for (int i = 0; i < initializeSystems.Count; i++) 45 | { 46 | initializeSystems[i].Initialize(); 47 | } 48 | } 49 | 50 | public virtual void Update() 51 | { 52 | for (int i = 0; i < updateSystems.Count; i++) 53 | { 54 | updateSystems[i].Update(); 55 | } 56 | } 57 | 58 | public virtual void Teardown() 59 | { 60 | for (int i = 0; i < teardownSystems.Count; i++) 61 | { 62 | teardownSystems[i].Teardown(); 63 | } 64 | } 65 | 66 | public virtual void Cleanup() 67 | { 68 | for (var i = 0; i < cleanupSystems.Count; i++) 69 | { 70 | cleanupSystems[i].Cleanup(); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Runtime/Systems.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33f10648b6aac864cb14e0ad77e0b2f3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Unity.LazyECS.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Unity.LazyECS", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": false, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [], 11 | "versionDefines": [], 12 | "noEngineReferences": false 13 | } -------------------------------------------------------------------------------- /Runtime/Unity.LazyECS.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f829098a39adf5641a5db89608bd75ee 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/World.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LazyECS.Component; 5 | using UnityEngine; 6 | 7 | namespace LazyECS 8 | { 9 | public abstract class World : IWorld, IDisposable 10 | { 11 | public Feature[] Features { get; protected set; } 12 | public Dictionary Entities { get; } //entity id, actual entity 13 | public List Groups { get; } 14 | 15 | public delegate void EntityCreatedEvent(Entity.Entity entity, bool entityCreatedFromNetworkMessage); 16 | public delegate void EntityDestroyedEvent(Entity.Entity entity, bool entityDestroyedFromNetworkMessage); 17 | public delegate void ComponentAddedToEntity(Entity.Entity entity, IComponent component); 18 | public delegate void ComponentRemovedFromEntity(Entity.Entity entity, IComponent component); 19 | public delegate void ComponentSetOnEntity(Entity.Entity entity, IComponent component, bool setFromNetworkMessage); 20 | 21 | public event EntityCreatedEvent OnEntityCreatedEvent; 22 | public event EntityDestroyedEvent OnEntityDestroyedEvent; 23 | public event ComponentAddedToEntity OnComponentAddedToEntityEvent; 24 | public event ComponentRemovedFromEntity OnComponentRemovedFromEntityEvent; 25 | public event ComponentSetOnEntity OnComponentSetOnEntityEvent; 26 | 27 | protected World() 28 | { 29 | Groups = new List(); 30 | Entities = new Dictionary(); 31 | Features = new Feature[]{}; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | for (int i = 0; i < Groups.Count; i++) 37 | { 38 | Groups[i]?.Dispose(); 39 | } 40 | 41 | foreach (KeyValuePair entity in Entities) 42 | { 43 | entity.Value.Dispose(); 44 | } 45 | 46 | OnEntityCreatedEvent = null; 47 | OnEntityDestroyedEvent = null; 48 | OnComponentAddedToEntityEvent = null; 49 | OnComponentRemovedFromEntityEvent = null; 50 | OnComponentSetOnEntityEvent = null; 51 | } 52 | 53 | public virtual void Initialize() 54 | { 55 | // Add features here in override 56 | } 57 | 58 | public virtual void Start() 59 | { 60 | for (int i = 0; i < Features.Length; i++) 61 | { 62 | Features[i].Initialize(); 63 | } 64 | } 65 | 66 | public virtual void Update() 67 | { 68 | for (int i = 0; i < Features.Length; i++) 69 | { 70 | Features[i].Update(); 71 | } 72 | } 73 | 74 | public virtual void Cleanup() 75 | { 76 | for (int i = 0; i < Features.Length; i++) 77 | { 78 | Features[i].Cleanup(); 79 | } 80 | } 81 | 82 | public virtual void Teardown() 83 | { 84 | for (int i = 0; i < Features.Length; i++) 85 | { 86 | Features[i].Teardown(); 87 | } 88 | } 89 | 90 | public virtual Entity.Entity CreateEntity(int id = -1, bool entityCreatedFromNetworkMessage = false) 91 | { 92 | Entity.Entity newEntity = id != -1 ? new Entity.Entity(id) : new Entity.Entity(Entities.Count); 93 | 94 | newEntity.OnComponentAdded += OnComponentAddedToEntity; 95 | newEntity.OnComponentRemoved += OnComponentRemovedFromEntity; 96 | newEntity.OnComponentSet += OnComponentSetOnEntity; 97 | Entities.Add(newEntity.id, newEntity); 98 | OnEntityCreated(newEntity, entityCreatedFromNetworkMessage); 99 | 100 | return newEntity; 101 | } 102 | 103 | public virtual bool DestroyEntity(int id, bool entityDestroyedFromNetworkMessage = false) 104 | { 105 | if (!Entities.ContainsKey(id)) 106 | { 107 | Debug.LogWarning("Attempted to destroy entity but it doesn't exist!"); 108 | return false; 109 | } 110 | 111 | return DestroyEntity(Entities[id], entityDestroyedFromNetworkMessage); 112 | } 113 | 114 | public virtual bool DestroyEntity(Entity.Entity entity, bool entityDestroyedFromNetworkMessage = false) 115 | { 116 | if (Entities.ContainsKey(entity.id)) 117 | { 118 | Entities.Remove(entity.id); 119 | OnEntityDestroyed(entity, entityDestroyedFromNetworkMessage); 120 | return true; 121 | } 122 | 123 | Debug.LogWarning("Attempted to destroy entity but it doesn't exist!"); 124 | return false; 125 | } 126 | 127 | public virtual void DestroyAllEntities() 128 | { 129 | foreach (KeyValuePair entity in Entities.ToList()) 130 | { 131 | DestroyEntity(entity.Value); 132 | } 133 | } 134 | 135 | public virtual Group CreateGroup(GroupType groupType, HashSet filters, EventType _eventType, Group.OnEntityUpdate OnEntityUpdate, bool checkExisting = true) 136 | { 137 | Group newGroup = new Group(groupType, filters, _eventType, OnEntityUpdate); 138 | Groups.Add(newGroup); 139 | 140 | if (checkExisting) 141 | CheckExisting(); 142 | 143 | void CheckExisting() 144 | { 145 | int entityCountAtStart = Entities.Count; 146 | foreach (KeyValuePair entity in Entities.ToArray()) 147 | { 148 | foreach (Type filter in filters) 149 | { 150 | if (entity.Value.Has(filter)) 151 | { 152 | newGroup.ComponentAddedToEntity(entity.Value, filter); 153 | } 154 | } 155 | } 156 | 157 | // This allows us to run the check for existing again in case entities were added or removed during the foreach loop 158 | if (Entities.Count != entityCountAtStart) 159 | CheckExisting(); 160 | } 161 | 162 | return newGroup; 163 | } 164 | 165 | /// 166 | /// Returns an entity with a specific ID 167 | /// 168 | /// The ID of the entity you want 169 | /// 170 | public Entity.Entity GetEntity(int id) 171 | { 172 | return Entities[id]; 173 | } 174 | 175 | public List GetEntities() 176 | { 177 | List entities = new List(); 178 | 179 | foreach (KeyValuePair entity in Entities) 180 | { 181 | entities.Add(entity.Value); 182 | } 183 | 184 | return entities; 185 | } 186 | 187 | /// 188 | /// Get a list of entities that have a given component 189 | /// 190 | /// 191 | /// 192 | public virtual List GetEntities() where TComponent : IComponent 193 | { 194 | List entitiesWithComponent = new List(); 195 | 196 | foreach (KeyValuePair entity in Entities) 197 | { 198 | if(entity.Value.Has()) 199 | entitiesWithComponent.Add(entity.Value); 200 | } 201 | 202 | return entitiesWithComponent; 203 | } 204 | 205 | /// 206 | /// Get a list of entities that have a given component with a specific value 207 | /// 208 | /// 209 | /// 210 | /// 211 | public virtual List GetEntities(object value) where TComponent : IComponent 212 | { 213 | List entitiesWithComponent = GetEntities(); 214 | List entitiesWithComponentValue = new List(); 215 | 216 | foreach (Entity.Entity entity in entitiesWithComponent) 217 | { 218 | if (entity.Get().Get().Equals(value)) 219 | entitiesWithComponentValue.Add(entity); 220 | } 221 | 222 | return entitiesWithComponentValue; 223 | } 224 | 225 | public virtual void OnEntityCreated(Entity.Entity entity, bool entityCreatedFromNetworkMessage) 226 | { 227 | if (SimulationController.Instance.LogLevel == LogLevel.Verbose) 228 | Debug.Log("[LazyECS] Creating entity with id " + entity.id + " in world " + GetType().Name + ""); 229 | 230 | OnEntityCreatedEvent?.Invoke(entity, entityCreatedFromNetworkMessage); 231 | } 232 | 233 | public virtual void OnEntityDestroyed(Entity.Entity entity, bool entityDestroyedFromNetworkMessage = false) 234 | { 235 | if (SimulationController.Instance.LogLevel == LogLevel.Verbose) 236 | { 237 | Debug.Log("[LazyECS] " + $"{entity.id} was DESTROYED in world {GetType().Name} "); 238 | } 239 | 240 | for (int i = 0; i < Groups.Count; i++) 241 | { 242 | Groups[i].EntityDestroyed(entity); 243 | } 244 | 245 | OnEntityDestroyedEvent?.Invoke(entity, entityDestroyedFromNetworkMessage); 246 | } 247 | 248 | public virtual void OnComponentAddedToEntity(Entity.Entity entity, IComponent component) 249 | { 250 | if (SimulationController.Instance.LogLevel == LogLevel.Verbose) 251 | { 252 | Debug.Log("[LazyECS] " + $"{component.GetType()} was ADDED to entity {entity.id} in world {GetType().Name} "); 253 | } 254 | 255 | for (int i = 0; i < Groups.Count; i++) 256 | { 257 | Groups[i].ComponentAddedToEntity(entity, component.GetType()); 258 | } 259 | 260 | OnComponentAddedToEntityEvent?.Invoke(entity, component); 261 | } 262 | 263 | public virtual void OnComponentRemovedFromEntity(Entity.Entity entity, IComponent component) 264 | { 265 | if (SimulationController.Instance.LogLevel == LogLevel.Verbose) 266 | Debug.Log("[LazyECS] " + $"{component.GetType()} was REMOVED from entity {entity.id} in world {GetType().Name} "); 267 | 268 | for (int i = 0; i < Groups.Count; i++) 269 | { 270 | Groups[i].ComponentRemovedFromEntity(entity, component.GetType()); 271 | } 272 | 273 | OnComponentRemovedFromEntityEvent?.Invoke(entity, component); 274 | } 275 | 276 | public virtual void OnComponentSetOnEntity(Entity.Entity entity, IComponent component, bool setFromNetworkMessage = false) 277 | { 278 | if (SimulationController.Instance.LogLevel == LogLevel.Verbose) 279 | Debug.Log("[LazyECS] " + $"{component.GetType()} was SET on entity {entity.id} in world {GetType().Name} "); 280 | 281 | for (int i = 0; i < Groups.Count; i++) 282 | { 283 | Groups[i].EntitySet(entity, component.GetType()); 284 | } 285 | 286 | OnComponentSetOnEntityEvent?.Invoke(entity, component, setFromNetworkMessage); 287 | } 288 | } 289 | } -------------------------------------------------------------------------------- /Runtime/World.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 384b94e65f87eb64dad288f6375afeb6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.kinematic.games.lazyecs", 3 | "version": "1.2.1", 4 | "displayName": "Lazy ECS", 5 | "description": "ECS for lazy devs.", 6 | "unity": "2017.1", 7 | "keywords": [ 8 | "ecs", 9 | "lazy" 10 | ], 11 | "author": { 12 | "name": "Tate McCormick", 13 | "email": "tate@kinematic.games", 14 | "url": "https://kinematic.games" 15 | } 16 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b6d800e85f18fe4a8d698050b4596f2 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------