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 classes.classPosition{publicintX,Y;}classVelocity{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{publicWorldWorld{get;set;}publicvoidRun(){// iterate sets of components.foreach(var(pos,vel)inWorld.Query<Position,Velocity>().Build()){pos.X+=vel.X;pos.Y+=vel.Y;}}}
Spawning / Despawning Entities
publicvoidRun(){// 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(){// 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 classes.classApples{}classLikes{}classOwes{publicintAmount;}
publicvoidRun(){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=World.GetComponent<Owes>(frank,bob);Console.WriteLine($"Frank owes Bob {owes.Amount} dollars");}
Queries
publicvoidRun(){// 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 componentsforeach(var(pos,vel)inquery){pos.Value+=vel.Value;}// You can create more complex, expressive queries with additional method chaining.// Here, we request every entity that has a Name component, owes money to Bob and does not have the Dead tag.varappleLovers=world.Query<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 int he loop.foreach(var(entity,name)inquery){Console.WriteLine($"Entity {entity} with name {name.Value} owes bob money and is still alive.")}}
Triggers
// Triggers are also just classes and very similar to components.// They act much like a simplified, ECS version of C# events.classMyTrigger{}
publicvoidRun(){// You can send a bunch of triggers inside of a system.World.Send(newMyTrigger());World.Send(newMyTrigger());World.Send(newMyTrigger());// In any system, including the origin system, you can now receive these triggers.foreach(vartinWorld.Receive<T>(this)){Console.WriteLine("It's a trigger!");}// Output:// It's a trigger!// It's a trigger!// It's a trigger!// NOTE: Triggers live until the end of the next frame, to make sure every system receives them.// Each trigger is always received exactly ONCE per system.}
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;usingRelEcs;usingWorld=RelEcs.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 RelEcs 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);}}