Tactics game and ECS engine/framework using Raylib for Chicken Scheme.
Find a file
2026-03-28 19:14:54 +08:00
engine Event buses (as hash tables for speed) 2026-03-28 19:14:54 +08:00
.dir-locals.el Initial commit and architecture 2026-03-26 18:17:36 +08:00
.gitignore Initial commit and architecture 2026-03-26 18:17:36 +08:00
README.org Event buses (as hash tables for speed) 2026-03-28 19:14:54 +08:00

Imugi

Imugi is a framework for building games (primarily tactics games) with Chicken Scheme and raylib. The end goal of this framework is to create a game, also called Imugi, and release to show the capability here.

Architecture

Imugi is an ECS engine.

Entities

Entities are game objects stored in an SRFI-69 hash table. A hash table is used for O(1) lookup. Entity keys within the hash table are symbols, either set intentionally or automatically generated via gensym. Entities are identified by their key, and are each a list of components. Entities are queued for addition and removal to/from the hash table world. Entity removal and addition is performed at the beginning of each frame, with removal occuring first.

There exists a hash map of SRFI-113 sets, component-sets. When an entity is added to world, every component of the entity is checked and the name of each component record is retrieved. The symbol representing the entity in world is then added to each set in the hash map associated with a component record type that the entity owns. This way, systems can query entities by the combination of components, using union operations. When an entity is removed from world, it is also removed from every component set.

Components

Components are SRFI-99 records which store data associated for a particular entity.

Systems

Systems are functions which modify state and draw to the screen. Systems are stored in the systems list as a record containing a name (symbol), rendering (a symbol), a priority (integer), entity criteria (list of symbols), and the function itself. Each system specifies the components an entity (the entity criteria) must have for it to be processed by that system. The entities are then selected for the system from the component-sets. Each system is applied to all entities which match its entity criteria. Systems have a priority which defaults to 0. Lower priority systems will be executed before higher priority systems. Systems within the same priority execute in order of addition to the systems list. If a system defines a rendering symbol of 3D, it will be wrapped in with-mode-3d. If a system defines a rendering symbol of 2D, it will be wrapped in with-mode-2d. All systems run in render loop, wrapped in with-drawing, and can draw non-2D and non-3D elements arbitrarily. In this way with-mode-3d and with-mode-2d can be avoided in system function bodies, and non-3D/2D systems can draw in screen space.

The systems list is sorted on each addition and removal of a system. Like entities, the addition of systems is queued for the beginning of each frame. This prevents the system list from being sorted multiple times per frame.

Systems can access resources via free variables or by being formed as closures. Systems only ever take an entity (a component list) as input.

Event Buses

An event bus is a hash table of symbols and records. They allow for delegating changes sent from other objects. The event record can contain any arbitrary data. The symbol the record is associated by is unique within the association list. Event records stay in the bus until removed by another system. Systems can push and pull events to and from the bus. Systems can choose to either pop the event off the bus (removing it from further processing) or just peek at the event. The symbol an event is associated by is essentially an "action". The event bus can have multiple events of the same type (for example, multiple different key presses can occur in a frame). Only one event of each name (for example, only 1 event can be called "jump", which might correspond to a space key press) is allowed in the bus at any one time. Custom event buses can be created and stored in the event-buses hash table. Systems can query a particular event bus within that list for a particular event.

Resources

When a resource, such as a font or texture, is loaded from the filesystem, the pointer is stored in an SRFI-69 hash table resources keyed by the path. When records are first loaded, a finalizer is set for them ensuring they are unloaded when collected by the GC. If the game attempts to load a resource which has already been loaded, it will use the pointer in the resources hash table instead.

Structure & Modules

The engine folder contains core engine code, organised across the following modules:

  • (engine core) which contains core functionality including starting the window and game loop, and functions for adding/removing systems, entities, and event buses to/from the game state.
  • (engine components) which contains methods for creating and accessing components.
  • (engine systems) which contains functions for creating new systems.
  • (engine events) which contains functions for creating and interacting with event buses.
  • (engine resources) which contains functions for loading and using resources.
  • (engine utilities) which contains generic engine utility functions.
  • (engine debug) which contains parameters and functions for debugging and profiling.
  • (engine math) which contains utility math and randomness functions.

The components folder contains pre-made components, entities, and systems for ease of use:

  • (components ui) contains building blocks for basic UI elements.
  • (components 2d) contains building blocks for basic 2D elements.

The games folder contains various sample games made with Imugi.

Dependencies

The following Chicken dependencies are required:

  • raylib
  • SRFI-99
  • SRFI-113
  • SRFI-69
  • csm