2026-10-10 12:07:33 -07:00
2026-10-09 13:47:22 -07:00
2026-10-10 12:07:33 -07:00
2026-10-09 21:26:00 -07:00
2026-10-09 13:47:22 -07:00

shoal

shoal logo

my silly little ecs engine for odin and raylib :3

contents

install

clone this repo in your project directory

git clone https://git.egfk.net/harper/shoal

and import the package

import ecs "shoal/src"

usage

first create a new app struct. this is where your systems and plugins live.

import ecs "shoal/src"

app: ecs.App

you can run your app with run(). run takes a reference to your app to run it's systems.

main :: proc() {
  ecs.run(&app)
}

systems

systems are procedures that run on set schedules

a schedule just determines when a producure runs
schedules are:

  • STARTUP
  • UPDATE
  • DRAW

STARTUP runs once at the start of an app
UPDATE runs every frame
DRAW runs every frame after update, and makes draw calls to raylib\

main :: proc() {
  ecs.add_system(&app, ecs.STARTUP, example_startup)
  ecs.add_system(&app, ecs.UPDATE, example_update)
}

example_startup :: proc() {
  // system that runs once
  // ...
}

example_update :: proc() {
  // system that runs every frame
  // ...
}

components

components are just structs with data, and kept with other components of that type in Storage.
create a new component

ExampleComponent :: struct {}

and create it's storage

Example: ecs.Storage(ExampleComponent)

spawn entities

create a new entity with spawn_entity()

main :: proc() {
  entity := ecs.spawn_entity(&app)
}

and give it new components with add_component()

main :: proc() {
  ecs.add_component(entity, &Example) // make sure to pass a reference to the storage, not the component type
}

query

since components are saved in storage next to their entity, you can loop through a Storage and pick out what you like

main :: proc() {
  for entity_id, component in ecs.slice(&Example) {
    //...
  }
}

or if you know your storage only has one entry use single()

main :: proc() {
  entity_id, component := ecs.single(&Example)
}

plugins

plugins are just a procedure that takes App as a parameter, meaning it can make changes to app, just like main()

ExamplePlugin :: proc(app: ^App) {
  ecs.add_system(app, ecs.STARTUP, some_plugin_system)
}
main :: proc() {
  ecs.add_plugin(&app, ecs.DefaultPlugin)
  ecs.add_plugin(&app, ExamplePlugin)
}

plugins make your scripts very reuseable. in fact, much of shoal's default behaviors come from plugins via DefaultPlugin()

S
Description
simple ecs game engine for odin built on raylib
Readme
193 KiB
Languages
Odin 100%