gokebiten

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 16 Imported by: 0

README

gokebiten

GOKe Logo
Go Version GoDoc License Codecov Coverage Go Quality Check

gokebiten integrates goke — a type-safe, archetype-based Entity Component System for Go — with Ebitengine. It wraps goke's ECS/System/Plan model behind a small Game type that implements ebiten.Game, and layers spatial indexing (via GOKg), kinematics, and collision resolution on top.

What's here

  • Game/Resources/TimeTracker (repo root) — wires a goke ECS into Ebitengine's Update/ Draw/Layout loop.
  • control/ — input capture and event dispatch.
  • physics/ — kinematics (movement, boundary handling) and collision detection/resolution (broad phase via gokg.Space, narrow phase, pluggable CollisionHandler strategies).
  • render/ — sprite batching, camera, tag-driven overlays, telemetry HUD.
  • spatial/ — population/spawn bookkeeping on top of a gokg.Space.

Installation

go get github.com/kjkrol/gokebiten

Example

examples/collision-demo — a real-time simulation of thousands of moving, colliding AABBs at a fixed 120 TPS, built entirely on this package plus goke's archetype-based storage and parallel systems.


Stats: 2306 colliding AABBs | 120 TPS | 50 collisions/tick

Stats: 524 colliding AABBs | 120 TPS | 15 collisions/tick

Run it locally:

make run

Prerequisites

Relationship to goke

This package used to live inside goke's examples/ebiten-demo; it has been extracted into its own repository so goke's core stays free of GUI dependencies while this integration can evolve (and version) independently. See goke for the ECS engine itself.

License

MIT — see LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ListSaves

func ListSaves(basePath string) ([]string, error)

ListSaves returns every save found for basePath: "" first if the quicksave (basePath+".game.save") exists, then every named save's label, alphabetically — pass any of them to Game.Load.

func RegComp

func RegComp[C any](game *Game) goke.CompID

Types

type Game

type Game struct {
	// contains filtered or unexported fields
}

func NewGame

func NewGame(res resources) *Game

func (*Game) Draw

func (g *Game) Draw(screen *ebiten.Image)

func (*Game) ECS

func (g *Game) ECS() *goke.ECS

func (*Game) Layout

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int)

func (*Game) Load

func (g *Game) Load(basePath, label string, space *gokg.Space, onLoaded func(count int), providers ...any) error

Load restores a snapshot written by Save (same basePath and label) into this (must be freshly constructed) Game: State, the ECS snapshot, and — since space (the gokg spatial index) isn't part of the ECS snapshot — a rebuild of space from every loaded entity's kinematics.Position, deferred to the same Setup phase RenderSequence/UseModule/Setup use. onLoaded, if not nil, is called once that rebuild completes, with the loaded entity count (e.g. to set a telemetry field) — nil if you don't need it. providers supplies component tokens the same way ProvidedComps does (e.g. your physics module); render.Appearance is always included.

func (*Game) Loop

func (g *Game) Loop(plan func(ctx goke.RunCtx, d time.Duration))

func (*Game) Pause

func (g *Game) Pause()

func (*Game) Paused

func (g *Game) Paused() bool

func (*Game) RegSys

func (g *Game) RegSys(factory func() goke.System) goke.Runnable

func (*Game) RenderSequence

func (g *Game) RenderSequence(rendererFactories ...func() render.Renderer)

func (*Game) Resume

func (g *Game) Resume()

func (*Game) Run

func (g *Game) Run()

func (*Game) Save

func (g *Game) Save(basePath, label string) error

Save pauses the ECS and writes one file — State (see Resources.SaveState; a zero-length marker if S doesn't implement encoding.BinaryMarshaler) followed by the ECS snapshot — to saveFilePath(basePath, label). label selects which save this is: "" for the quicksave slot, anything else for a named save (see ListSaves to discover named saves already on disk). Resumes before returning either way.

func (*Game) SetEventHandler

func (g *Game) SetEventHandler(handler control.EventHandler)

func (*Game) Setup

func (g *Game) Setup(providers ...goke.SetupProvider)

Setup defers each provider's SetupSystems to the same one-time ecs.Setup call RenderSequence/UseModule feed — in call order, mirroring ecs.Setup's own name and one-time-seeding spirit at the Game level.

func (*Game) Step

func (g *Game) Step() time.Duration

func (*Game) TogglePause

func (g *Game) TogglePause()

func (*Game) Update

func (g *Game) Update() error

func (*Game) UseModule

func (g *Game) UseModule(m goke.Module)

UseModule defers m.RegSystems to the same Setup call RenderSequence/Setup use, in call order — call it after any spatial.WorldModule that seeds entities m needs to already exist (e.g. a physics module scanning for Sensor tags at registration).

type GameProps

type GameProps struct {
	Title                     string
	TargetTPS                 int
	ScreenWidth, ScreenHeight int
}

type Resettable

type Resettable interface{ Reset() }

Resettable lets Telemetry hook into each stats interval — if T implements it, Reset is called right after TPS is refreshed.

type Resources

type Resources[S, T any] struct {
	// contains filtered or unexported fields
}

Resources bundles what Game needs (GameProps, input, world config, and built-in TPS/EntityCount telemetry) with two game-owned shapes: S for control state, T for display telemetry.

func NewResources

func NewResources[S, T any](gameProps *GameProps, spaceConfig spatial.Config, state S, telemetry T) *Resources[S, T]

func (*Resources[S, T]) GetGameProps

func (r *Resources[S, T]) GetGameProps() *GameProps

func (*Resources[S, T]) GetInputEvents

func (r *Resources[S, T]) GetInputEvents() *control.InputEvents

func (*Resources[S, T]) GetSpaceConfig

func (r *Resources[S, T]) GetSpaceConfig() spatial.Config

func (*Resources[S, T]) LoadState

func (r *Resources[S, T]) LoadState(rd io.Reader) error

LoadState restores State from rd, written by SaveState — a no-op (beyond consuming the length prefix, to leave rd positioned right after State for any data that follows) if S doesn't implement encoding.BinaryUnmarshaler.

func (*Resources[S, T]) Reset

func (r *Resources[S, T]) Reset()

func (*Resources[S, T]) SaveState

func (r *Resources[S, T]) SaveState(w io.Writer) error

SaveState writes a uint32 length prefix followed by State to w — the prefix is 0 if S doesn't implement encoding.BinaryMarshaler, so games with nothing worth persisting beyond the ECS snapshot don't need to do anything special. Length-prefixed (rather than reading w to EOF) so SaveState/LoadState can share a stream with data written after them, e.g. Game.Save appending an ECS snapshot to the same file.

func (*Resources[S, T]) State

func (r *Resources[S, T]) State() *S

func (*Resources[S, T]) TPS

func (r *Resources[S, T]) TPS() *int

func (*Resources[S, T]) Telemetry

func (r *Resources[S, T]) Telemetry() *T

type TimeTracker

type TimeTracker struct {
	// contains filtered or unexported fields
}

TimeTracker is responsible for the fixed physics step (Fixed Time Step) and statistics.

func NewTimeTracker

func NewTimeTracker() *TimeTracker

func (*TimeTracker) CalculateSteps

func (t *TimeTracker) CalculateSteps(physicsStep time.Duration, maxSteps int) int

CalculateSteps calculates how many physics ticks should be performed in the current frame.

func (*TimeTracker) ProcessStatsInterval

func (t *TimeTracker) ProcessStatsInterval() bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL