ArepyEngine is the high-level entry point for creating windows, worlds, renderers, input, audio, and shared resources.
Creating an engine is usually the first thing you do. In many cases, a title and the default settings are enough to get going.
engine = ArepyEngine(title="My Game")The constructor signature is:
ArepyEngine(
title: str = "Arepy Engine",
width: int = 1920 // 3,
height: int = 1080 // 3,
max_frame_rate: int = 800,
fullscreen: bool = False,
icon_path: PathLike[str] | None = None,
window_flags: WindowFlag | None = None,
)If you want more control, you can also pass:
title: strsets the window title shown in the title barwidth: intsets the window width in pixels when the window is createdheight: intsets the window height in pixels when the window is createdmax_frame_rate: intis passed toRenderer2D.set_max_framerate(...)during engine startupfullscreen: booldecides whetherDisplay.toggle_fullscreen()is called right after the window opensicon_path: PathLike[str] | Noneis passed toDisplay.set_window_icon(...)when you want a custom window iconwindow_flags: WindowFlag | Noneis passed toDisplay.set_window_state(...)before the window is created
Here is a more explicit example:
from pathlib import Path
from arepy import ArepyEngine, WindowFlag
engine = ArepyEngine(
title="Space Garden",
width=1280,
height=720,
max_frame_rate=144,
fullscreen=False,
icon_path=Path("assets/icon.png"),
window_flags=WindowFlag.VSYNC_HINT,
)All of these values are passed as normal Python keyword arguments when you create the engine instance.
When the engine starts, it opens the window and makes these shared services available across the app:
DisplayTimeRenderer2DRenderer3DAssetStoreInputArepyEngineAudioDeviceEventManagerimgui(optional)
That lets you create a world and start adding systems without having to wire every subsystem by hand.
If you install the optional ImGui extra, the engine also exposes imgui and manages its frame lifecycle automatically.
Each new world also starts with a local Timers resource.
A world is a named container around an ECS Registry.
The methods you usually call are:
world = engine.create_world(name: str)
engine.set_current_world(name: str)engine = ArepyEngine(title="My Game")
world = engine.create_world("main")
engine.set_current_world("main")create_world(name: str) expects the world name as a string and returns a new World.
set_current_world(name: str) also expects the world name as a string. You pass the name of a world that was already created.
create_world(name) creates a World that can see the engine's shared services, while still keeping room for world-specific resources and callbacks. That world also receives a built-in Timers resource, so delayed and repeating callbacks are available immediately.
The runtime loop implemented in ArepyEngine.run() currently works like this:
- call
on_startup()once - while the window stays open:
- process the next frame
- apply any deferred world switch
- call
on_shutdown()once
Inside a frame, the order is:
- advance
TimefromDisplay.get_time() INPUTpipeline- tick the current world's
Timers - tick the current world's
Animator - process queued
EventManagerevents - registry
update() UPDATEpipeline- world
on_update()hooks - engine
on_update()hook - process queued
EventManagerevents again - start a new ImGui frame if ImGui is enabled
RENDERpipelineRENDER_UIpipeline- world
on_render()hooks - engine
on_render()hook - finish ImGui and draw it if ImGui is enabled
- renderer buffer swap
If you are using ImGui, the main idea is simple: write widgets in RENDER_UI and let the engine handle the setup and final draw.
Besides the engine-level hooks, each world can register its own lifecycle callbacks.
world = engine.create_world("main")
@world.on_startup
def load_scene() -> None:
...
@world.on_update
def update_hud() -> None:
...
@world.on_render
def draw_debug_overlay() -> None:
...
@world.on_shutdown
def release_scene() -> None:
...These hooks are useful when you want a little world-specific setup or teardown without creating a dedicated ECS system for it.
on_startupruns when that world becomes the current worldon_updateruns once per frame after theUPDATEpipelineon_renderruns once per frame afterRENDERandRENDER_UIon_shutdownruns when you leave that world or when the engine closes
on_update and on_render are optional. They are most useful for glue code, scene orchestration, UI state, or one-off world behaviors that do not fit naturally into a regular ECS system.
The enum currently defines these phases:
UPDATERENDERINPUTPHYSICSASYNC_UPDATERENDER_UI
Only the phases explicitly called by ArepyEngine are part of the default frame loop today. If you depend on an additional pipeline such as PHYSICS, you currently need to orchestrate it through your own systems or engine customization.
set_current_world(name) does not switch immediately. It stores the next world name and applies the change after the current frame step. When the switch happens, the previous world receives on_shutdown, and the new world receives on_startup.