You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A lightweight and easy to use entity component system with an effective feature set for making games.
Components
// Components are simple structs.structPosition{publicintX,Y;}structVelocity{publicintX,Y;}
Systems
// Systems add all the functionality to the Entity Component System.// Usually, you would run them from within your game loop.publicclassMoveSystem:ISystem{publicvoidRun(Worldworld){// iterate sets of components.varquery=world.Query<Position,Velocity>().Build();query.Run((count,positions,velocities)=>{for(vari=0;i<count;i++){positions[i].X+=velocities[i].X;positions[i].Y+=velocities[i].Y;}});}}
Spawning / Despawning Entities
publicvoidRun(Worldworld){// Spawn a new entity into the world and store the id for later useEntityentity=world.Spawn().Id();// Despawn an entity.world.Despawn(entity);}
Adding / Removing Components
publicvoidRun(Worldworld){// Spawn an entity with componentsEntityentity=world.Spawn().Add(newPosition()).Add(newVelocity{X=5}).Add<Tag>().Id();// Change an Entities Componentsworld.On(entity).Add(newName{Value="Bob"}).Remove<Tag>();}
Relations
// Like components, relations are structs.structApples{}structLikes{}structOwes{publicintAmount;}
publicvoidRun(Worldworld){varbob=world.Spawn().Id();varfrank=world.Spawn().Id();// Relations consist of components, associated with a "target".// The target can either be another component, or an entity.world.On(bob).Add<Likes>(typeof(Apples));// Component ^^^^^^^^^^^^^^world.On(frank).Add(newOwes{Amount=100},bob);// Entity ^^^// if you want to know if an entity has a componentbooldoesBobHaveApples=world.HasComponent<Apples>(bob);// if you want to know if an entity has a relationbooldoesBobLikeApples=world.HasComponent<Likes>(bob,typeof(Apples));// Or get it directly.// In this case, we retrieve the amount that Frank owes Bob.varowes=this.GetComponent<Owes>(frank,bob);Console.WriteLine($"Frank owes Bob {owes.Amount} dollars");}
Queries
publicvoidRun(Worldworld){// With queries, we can get a list of components that we can iterate through.// A simple query looks like thisvarquery=world.Query<Position,Velocity>().Build();// Now we can loop through these componentsquery.Run((count,positions,velocities)=>{for(vari=0;i<count;i++){positions[i].X+=velocities[i].X;positions[i].Y+=velocities[i].Y;}});// we can also iterate through them using multithreading!// for that, we simply replace `Run` with `RunParallel`// note that HypEcs is an arche type based ECS.// when running iterations multithreaded, that means we parallelise each *Table* in the ecs,// not each component iteration. This means MultiThreading benefits from archetype fragmentation,// but does not bring any benefits when there is only one archetype existing in the ecs that is iterated.query.RunParallel((count,positions,velocities)=>{for(vari=0;i<count;i++){positions[i].X+=velocities[i].X;positions[i].Y+=velocities[i].Y;}});// You can create more complex, expressive queries through the QueryBuilder.// Here, we request every entity that has a Name component, owes money to Bob and does not have the Dead tag.varappleLovers=world.QueryBuilder<Entity,Name>().Has<Owes>(bob).Not<Dead>().Build();// Note that we only get the components inside Query<>.// Has<T>, Not<T> and Any<T> only filter, but we don't actually get T in the loop.appleLovers.Run((count,entities,names)=>{for(vari=0;i<count;i++){Console.WriteLine($"Entity {entities[i]} with name {names[i].Value} owes bob money and is still alive.")}});}
Creating a World
// A world is a container for different kinds of data like entities & components.Worldworld=newWorld();
Running a System
// Create an instance of your system.varmoveSystem=newMoveSystem();// Run the system.// The system will match all entities of the world you enter as the parameter.moveSystem.Run(world);// You can run a system as many times as you like.moveSystem.Run(world);moveSystem.Run(world);moveSystem.Run(world);// Usually, systems are run once a frame, inside your game loop.
SystemGroups
// You can create system groups, which bundle together multiple systems.SystemGroupgroup=newSystemGroup();// Add any amount of systems to the group.group.Add(newSomeSystem()).Add(newSomeOtherSystem()).Add(newAThirdSystem());// Running a system group will run all of its systems in the order they were added.group.Run(world);
Example of a Game Loop
// In this example, we are using the Godot Engine.usingGodot;usingHypEcs;usingWorld=HypEcs.World;// Godot also has a World class, so we need to specify this.publicclassGameLoop:Node{Worldworld=newWorld();SystemGroupinitSystems=newSystemGroup();SystemGrouprunSystems=newSystemGroup();SystemGroupcleanupSystems=newSystemGroup();// Called once on node construction.publicGameLoop(){// Add your initialization systems.initSystem.Add(newSomeSpawnSystem());// Add systems that should run every frame.runSystems.Add(newPhysicsSystem()).Add(newAnimationSystem()).Add(newPlayerControlSystem());// Add systems that are called once when the Node is removed.cleanupSystems.Add(newDespawnSystem());}// Called every time the node is added to the scene.publicoverridevoid_Ready(){// Run the init systems.initSystems.Run(world);}// Called every frame. Delta is time since the last frame.publicoverridevoid_Process(floatdelta){// Run the run systems.runSystems.Run(world);// IMPORTANT: For HypEcs to work properly, we need to tell the world when a frame is done.// For that, we call Tick() on the world, at the end of the function.world.Tick();}// Called when the node is removed from the SceneTree.publicoverridevoid_ExitTree(){// Run the cleanup systems.cleanupSystems.Run(world);}}