8000 GitHub - jneums/geecs: Ghost Engine ECS (GEECS)
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

jneums/geecs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ghost Engine ECS (GEECS)

mops documentation

Overview

GEECS is a simple Entity Component System (ECS) library for Motoko. It is designed to be simple to use and easy to integrate into your existing projects.

You can use GEECS to create entities with components, and run systems that update the components of those entities. This allows you to create complex systems with minimal boilerplate.

It was created as part of the Ghost Engine project, a simple game engine for the Internet Computer. Ghost Engine uses an authoritative server model, where the server runs the game simulation and sends updates to clients. GEECS is used to manage the game state and run the game simulation on the Internet Computer.

Online Demo

There is a demo that uses GEECS to create a simple 3d ICRC1 token mining game.

Check it out on Github here, or play it on the Internet Computer here.

Usage

Install with mops

You can install GEECS using the mops package manager. To install GEECS, run the following command:

mops add geecs

Full Example

Here is a full example of how to use GEECS to create a simple 3D movement system. In this example, we define a simple 3D vector type to represent positions and velocities, and create components for positions and velocities. We then create a system that moves entities that have both a position and velocity component.

Check out the tests to see an example of how to use GEECS.

// main.mo
import ECS "mo:geecs";

// Define a simple 3D vector type to represent positions and velocities:
type Vector3 = {
  x : Nat;
  y : Nat;
  z : Nat;
};

// Define components required for movement (plain data types):
type PositionComponent = {
  position : Vector3;
};

type VelocityComponent = {
  velocity : Vector3;
};

// Add the components to the ECS component type:
type Component = {
  #PositionComponent : PositionComponent;
  #VelocityComponent : VelocityComponent;
};

// Initialize the entity counter:
var entityCounter : Nat = 0;

// Initialize the required ECS data structures:
let ctx : ECS.Types.Context<Component> = {
  entities = ECS.State.Entities.new<Component>();
  registeredSystems = ECS.State.SystemRegistry.new<Component>();
  systemsEntities = ECS.State.SystemsEntities.new();
  updatedComponents = ECS.State.UpdatedComponents.new<Component>();
  entitiesToDelete = ECS.State.EntitiesToDelete.new();

  // Incrementing entity counter for ids.
  nextEntityId = func() : Nat {
    entityCounter += 1;
    entityCounter;
  };
};

// Create a system that moves entities that have both a position and velocity component:
let movementArchetype = ["PositionComponent", "VelocityComponent"];
let MovementSystem : ECS.Types.System<Component> = {
  systemType = "MovementSystem";
  archetype = movementArchetype;
  update = func(ctx : ECS.Types.Context<Component>, entityId : Nat, deltaTime : Time.Time) : async () {
    let position = ECS.World.getComponent<Component>(ctx, entityId, "PositionComponent");
    let velocity = ECS.World.getComponent<Component>(ctx, entityId, "VelocityComponent");

    switch (position, velocity) {
      case (? #PositionComponent({ position }), ? #VelocityComponent({ velocity })) {
        // Update the position based on the velocity
        let updated = {
          x = position.x + velocity.x;
          y = position.y + velocity.y;
          z = position.z + velocity.z;
        };

        let updatedComponent = #PositionComponent({ position = updated });
        ECS.World.addComponent<Component>(ctx, entityId, "PositionComponent", updatedComponent);
      };
      case (_) ();
    };
  };
};

// Register the system to enable it to be run:
ECS.World.addSystem<Component>(ctx, MovementSystem);


// Create an entity with a position and velocity component:
let entityId = ECS.World.addEntity<Component>(ctx);
let position = #PositionComponent({ position = { x = 0; y = 0; z = 0 } });
ECS.World.addComponent<Component>(ctx, entityId, "PositionComponent", position);
let velocity = #VelocityComponent({ velocity = { x = 1; y = 1; z = 1 } });
ECS.World.addComponent<Component>(ctx, entityId, "VelocityComponent", velocity);

// Process all systems and save delta time
let thisTick = Time.now();
let deltaTime = thisTick - lastTick;
await ECS.World.update(ctx, deltaTime);
lastTick := thisTick;

Updates will be stored in the updatedComponents field of the context, and can be used to sync with clients or other systems.

Game Loop:

Here is an example of how to create a game loop that runs the simulation and sends updates to clients:

// Game loop runs all the systems
func gameLoop() : async () {
  // Process all systems and save delta time
  let thisTick = Time.now();
  let deltaTime = thisTick - lastTick;
  await ECS.World.update(ctx, deltaTime);
  lastTick := thisTick;

  // Remove all updates older than 5 seconds
  let fiveSecondsAgo = thisTick - 30 * 1_000_000_000;
  let updated = Updates.filterByTimestamp(ctx.updatedComponents, fiveSecondsAgo);
  Vector.clear(ctx.updatedComponents);

  for (update in Vector.vals(updated)) {
    Vector.add(ctx.updatedComponents, update);
  };
};

// Will tick every block
let gameTick = #nanoseconds(1_000_000_000 / 60);
ignore Timer.recurringTimer<system>(gameTick, gameLoop);

About

Ghost Engine ECS (GEECS)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

0