Skip to content

Engine services at a glance

Systems usually need more than components. They need time, input, rendering, audio, assets, or shared game state. Arepy makes those objects available as typed resources, so a function's parameters explain its dependencies.

def pause_menu(
    input_device: Input,
    audio: AudioDevice,
    time: Time,
) -> None:
    ...

There is no service locator call inside the system and no setup on every frame. Arepy resolves the arguments when the system runs.

The services you will use first

Service What it answers Learn more
Time How long was the frame? How long has the game run? Lifecycle
Input Which keys, buttons, sticks, or mouse actions are active? Input
Renderer2D What should be drawn in this frame? 2D graphics
AssetStore Where is the already-loaded texture, sound, or model? 2D graphics
AudioDevice Play and control sounds or music. Audio
Display Window size, title, fullscreen, cursor, and monitors. Engine API
Renderer3D Draw models, meshes, billboards, and 3D primitives. Built-in bundle
EventManager Queue messages between otherwise independent features. Resources
imgui Build optional development panels. ImGui

Time

Use the global Time resource for frame-independent gameplay:

def movement_system(
    query: Query[Entity, With[Transform, RigidBody2D]],
    time: Time,
) -> None:
    for transform, body in query.iter_components(Transform, RigidBody2D):
        transform.position.x += body.velocity.x * time.delta_seconds
        transform.position.y += body.velocity.y * time.delta_seconds

delta_seconds is the elapsed game time for this frame. elapsed_seconds is the accumulated game time. time_scale lets slow motion or pausing affect the world-local timers and animator as well.

Timers

Every world owns a Timers resource. Use it for a delayed callback, a repeated action, or a cooldown without comparing timestamps in several systems.

from arepy import Timers


def weapon_system(timers: Timers) -> None:
    if timers.cooldown("player.fire", 0.20):
        fire_projectile()

For setup-time scheduling:

@world.on_startup
def schedule_wave() -> None:
    timers = world.get_world_resource(Timers)
    timers.after(1.0, spawn_first_wave)
    timers.every(5.0, spawn_next_wave)

Timer names should describe ownership ("player.fire", "level.next_wave") to avoid accidental collisions inside a world.

Animator

Every world also owns one Animator. It is useful for small scripted transitions, not a replacement for a full skeletal animation tool.

@world.on_startup
def fade_in() -> None:
    animator = world.get_world_resource(Animator)
    (
        animator.create()
        .to(hud_state, "opacity", 255, 0.35)
        .call(show_ready_message)
        .start()
    )

Property access is prepared when the timeline is built. Keep timeline creation out of per-entity hot loops; start a timeline in response to an actual event.

Display

Display controls the window rather than drawing its contents:

def toggle_fullscreen(input_device: Input, display: Display) -> None:
    if input_device.is_key_pressed(Key.F11):
        display.toggle_fullscreen()

Window creation flags, such as VSync and resizability, are passed to ArepyEngine:

engine = ArepyEngine(
    title="My game",
    width=1280,
    height=720,
    window_flags=(
        WindowFlag.VSYNC_HINT | WindowFlag.WINDOW_RESIZABLE
    ),
)

Global or world-local?

Renderer, input, audio, assets, display, time, engine, and events are global: all worlds can use the same instance. Timers, animator, and your scene state are usually world-local. A world-local resource with the same type takes precedence for systems in that world.

Read Resources and systems for custom state and ownership.