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.
World
// A world is a container for different kinds of data like entities & components.Worldworld=newWorld();
Entity
// Spawn a new entity into the world.Entityentity=world.Spawn();// Despawn an entity.entity.Despawn();
Component
// Components are simple classes.classPosition{publicintX,Y;}classVelocity{publicintX,Y;}// Add new components to an entity.entity.Add<Position>().Add(newVelocity{X=1,Y=0});// Get a component from an entity.varvel=entity.Get<Velocity>();// Remove a component from an entity.entity.Remove<Position>();
Element
// Elements are unique class-based components that are attached directly to worlds.classSavePath{stringValue;}// Add an element to the world.// You can only have one element per type in a world.world.AddElement(newSavePath(Value="user://saves/"));// Get an element from the world.varsavePath=world.GetElement<SavePath>();Console.WriteLine(savePath.Value);// Remove an element from the world.world.RemoveElement<SavePath>();
Relation
// Like components, relations are classes.classLikes{}classOwes{publicintAmount;}classApples{}varbob=world.Spawn();varfrank=world.Spawn();// Relations consist of components, associated with a "target".// The target can either be another component, or an entity.bob.Add<Likes>(typeof(Apples));// Component ^^^^^^^^^^^^^^frank.Add(newOwes{Amount=100},bob);// Entity ^^^// You can test if an entity has a component or a relation.booldoesBobHaveApples=bob.Has<Apples>();booldoesBobLikeApples=bob.Has<Likes>(typeof(Apples));// Or get it directly.// In this case, we retrieve the amount that Frank owes Bob.varowes=frank.Get<Owes>(bob);Console.WriteLine($"Frank owes Bob {owes.Amount} dollars");
Commands
// Commands are a wrapper around World that provide additional helpful functions.Commandscommands=newCommands(world);// You *do not* need to create your own commands.// They will be automatically provided for you as the System.Run(Commands) argument.
Query
// With queries, we can get a list of components that we can iterate through.// A simple query looks like thisvarquery=commands.Query<Position,Velocity>();// Now we can loop through these componentsforeach(var(pos,vel)inquery){pos.Value+=vel.Value;}// You can create more complex, expressive queries.// Here, we request every entity that has a Name component, owes money to Bob and does not have the Dead tag.varappleLovers=commands.Query<Name>().Has<Owes>(bob).Not<Dead>();// 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(varnameinquery){Console.WriteLine($"{name.Value} owes bob money and is still alive.")}
System
// Systems add all the functionality to the Entity Component System.// Usually, you would run them from within your game loop.publicclassMoveSystem:ISystem{publicvoidRun(Commandscommands){// Query desired components.varquery=commands.Query<Position,Velocity>();// Loop over queried of components.foreach(var(pos,vel)inquery){pos.Value+=vel.Value;}// You can also access the entity within the loop.varquery=commands.Query<Entity,Position,Velocity>();foreach(var(entity,pos,vel)inquery)=>{pos.Value+=vel.Value;// Example: "Tag" a component to show that it has moved.entity.Add<Moved>();}}}
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.
Triggers
// Triggers are also just structs and very similar to components.// They act much like a simplified, ECS version of C# events.classMyTrigger{}// You can send a bunch of triggers inside of a system.commands.Send<MyTrigger>();commands.Send<MyTrigger>();commands.Send<MyTrigger>();// In any system, including the origin system, you can now receive these triggers.commands.Receive((MyTriggere)=>{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.
SystemGroup
// 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);}}