core.components package

Submodules

core.components.animator module

class core.components.animator.AnimatorComponent(controller_path: str = None, play_on_start: bool = True, speed: float = 1.0)[source]

Bases: Component

consume_trigger(trigger_name: str)[source]
get_current_frame()[source]
keep_only_triggers(valid_triggers: set[str])[source]
load_controller(path: str, preserve_state: bool = False)[source]
pause()[source]
play(state_name: str, restart: bool = False)[source]
reload_controller_if_changed()[source]
resume()[source]
set_trigger(trigger_name: str)[source]
stop(reset: bool = False)[source]

core.components.camera module

class core.components.camera.CameraComponent(active: bool = True, zoom: float = 1.0, rotation: float = 0.0, viewport_x: float = 0.0, viewport_y: float = 0.0, viewport_width: float = 1.0, viewport_height: float = 1.0, priority: int = 0, follow_target_id: str = '', follow_rotation: bool = True)[source]

Bases: Component

DECAY_EXPONENTIAL = 'exponential'
DECAY_LINEAR = 'linear'
property is_shaking: bool
shake(intensity: float = 5.0, duration: float = 0.3, decay: str = 'linear')[source]

Start a camera shake effect.

Parameters:
  • intensity – Maximum pixel offset per axis.

  • duration – How long the shake lasts in seconds.

  • decay – Decay curve — "linear" or "exponential".

property shake_offset: tuple[float, float]

Current (x, y) shake offset in pixels.

update_shake(dt: float)[source]

Advance the shake timer and compute the current offset. Called by the render system each frame.

core.components.colliders module

class core.components.colliders.BoxCollider2D(width: float = None, height: float = None, offset_x: float = 0.0, offset_y: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295, rotation: float = 0.0)[source]

Bases: Component

property offset_x
property offset_y
class core.components.colliders.CircleCollider2D(radius: float = None, offset_x: float = 0.0, offset_y: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295, rotation: float = 0.0)[source]

Bases: Component

property offset_x
property offset_y
class core.components.colliders.PolygonCollider2D(points: list | None = None, offset_x: float = 0.0, offset_y: float = 0.0, rotation: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295)[source]

Bases: Component

property offset_x
property offset_y
property points

core.components.http_client module

class core.components.http_client.HTTPClientComponent(base_url: str = '', default_headers: dict = None, timeout: float = 30.0, max_concurrent: int = 4)[source]

Bases: Component

Persistent HTTP client component for making async HTTP requests.

Uses a background thread pool to perform requests without blocking the game loop. Results are queued and retrieved via poll().

Usage in scripts:

http = self.entity.get_component(HTTPClientComponent)

# GET request http.get(”https://api.example.com/data”, tag=”fetch_data”)

# POST request with JSON body http.post(”https://api.example.com/submit”,

body={“key”: “value”}, tag=”submit”)

# In on_update, poll for completed responses for response in http.poll():

if response.ok:

data = response.json() print(f”[{response.tag}] Got: {data}”)

else:

print(f”[{response.tag}] Error: {response.error}”)

delete(url: str, headers: dict = None, tag: str = '')[source]

Perform an async DELETE request.

get(url: str, headers: dict = None, tag: str = '')[source]

Perform an async GET request.

get_pending_count() int[source]

Return number of in-flight requests.

on_destroy()[source]

Wait briefly for pending threads to finish.

patch(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async PATCH request.

poll() list[HTTPResponse][source]

Drain the response inbox. Call this in on_update. Returns a list of HTTPResponse objects for completed requests.

post(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async POST request. Body can be str, bytes, or dict (sent as JSON).

put(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async PUT request.

class core.components.http_client.HTTPResponse(status_code: int = 0, body: str = '', headers: dict = None, error: str = '', tag: str = '')[source]

Bases: object

Represents an HTTP response returned by HTTPClientComponent.

body
error
headers
json()[source]

Parse body as JSON. Returns None on failure.

property ok: bool
status_code
tag

core.components.http_request module

class core.components.http_request.HTTPRequestComponent(url: str = '', method: str = 'GET', request_body: str = '', content_type: str = 'application/json', timeout: float = 30.0, send_on_start: bool = False)[source]

Bases: Component

Lightweight single-request component. Configure in the inspector or script, fire it, and poll for the result.

Usage in scripts:

req = self.entity.get_component(HTTPRequestComponent)

# Configure and send req.url = “https://api.example.com/data” req.method = “GET” req.send()

# In on_update, check if done if req.is_done():

print(req.status_code, req.response_body) if req.ok:

data = req.json()

# Or send with body req.url = “https://api.example.com/submit” req.method = “POST” req.request_body = ‘{“key”: “value”}’ req.send()

METHOD_DELETE = 'DELETE'
METHOD_GET = 'GET'
METHOD_PATCH = 'PATCH'
METHOD_POST = 'POST'
METHOD_PUT = 'PUT'
is_done() bool[source]

Return True if the request has completed (success or error).

is_sending() bool[source]

Return True if a request is currently in flight.

json()[source]

Parse response_body as JSON. Returns None on failure.

property ok: bool

Return True if the last request was successful (2xx).

on_destroy()[source]
reset()[source]

Reset the response state for reuse.

send()[source]

Fire the HTTP request in a background thread.

core.components.light module

2D Light components for point lights, spot lights, and light occluders.

Attach a PointLight2D or SpotLight2D to an entity with a Transform to create a light source. Add LightOccluder2D to entities that should block light and cast shadows.

The LightingSystem renders an ambient + light overlay each frame.

Usage:

entity = world.create_entity("torch")
entity.add_component(Transform(x=100, y=200))
entity.add_component(PointLight2D(color=(255, 200, 100), radius=300, intensity=1.0))

wall = world.create_entity("wall")
wall.add_component(Transform(x=300, y=200))
wall.add_component(LightOccluder2D(shape="box", width=50, height=200))
class core.components.light.LightOccluder2D(shape: str = 'box', width: float = 50.0, height: float = 50.0, radius: float = 25.0, points: list | None = None, offset_x: float = 0.0, offset_y: float = 0.0, receive_light: bool = False, receive_shadow: bool = False, rotation: float = 0.0)[source]

Bases: Component

Light occluder that blocks light and casts shadows.

Supports three shape types: - "box" — axis-aligned rectangle (width × height) - "circle" — circle defined by radius - "polygon" — arbitrary convex/concave polygon (list of Vector2 points)

When added via the inspector with shape "box" or "circle", the size is automatically derived from the entity’s SpriteRenderer if present.

SHAPE_BOX = 'box'
SHAPE_CIRCLE = 'circle'
SHAPE_POLYGON = 'polygon'
property points
class core.components.light.PointLight2D(color: tuple = (255, 255, 255), radius: float = 200.0, intensity: float = 1.0, falloff: float = 2.0)[source]

Bases: Component

Omnidirectional 2D point light.

class core.components.light.SpotLight2D(color: tuple = (255, 255, 255), radius: float = 300.0, intensity: float = 1.0, falloff: float = 2.0, angle: float = 0.0, cone_angle: float = 45.0, offset_x: float = 0.0, offset_y: float = 0.0)[source]

Bases: Component

Directional 2D spot light with a cone angle.

core.components.multiplayer module

class core.components.multiplayer.MultiplayerComponent(player_name: str = 'Player', max_players: int = 8, sync_rate: float = 20.0, port: int = 8765)[source]

Bases: Component

High-level multiplayer manager component.

Attach to a single entity (e.g. a GameManager) to manage multiplayer sessions. Uses a WebSocketComponent on the same entity for transport. Provides lobby management, player tracking, RPCs, and state sync.

Modes:
  • “host”: Creates a WebSocket server, manages the room as authority.

  • “client”: Connects to a host’s WebSocket server.

Usage in scripts:

mp = self.entity.get_component(MultiplayerComponent)

# Host a game mp.host_game(“MyRoom”)

# Or join a game mp.join_game(“ws://192.168.1.10:8765”, “PlayerName”)

# In on_update, pump the network mp.poll()

# Check players for player in mp.get_players():

print(player.name, player.is_ready)

# Set ready mp.set_ready(True)

# Start game (host only, when all ready) mp.start_game()

# Send RPC to all players mp.rpc(“take_damage”, {“amount”: 10})

# Send RPC to specific player mp.rpc_to(player_id, “heal”, {“amount”: 5})

# Send RPC to host only mp.rpc_to_host(“request_spawn”, {“prefab”: “bullet”})

# Send custom data mp.send_custom(“chat”, {“text”: “Hello!”})

# Listen for events (via global event system) self.subscribe_to_event(“mp_player_joined”, self.on_player_joined) self.subscribe_to_event(“mp_player_left”, self.on_player_left) self.subscribe_to_event(“mp_game_started”, self.on_game_started) self.subscribe_to_event(“mp_rpc”, self.on_rpc) self.subscribe_to_event(“mp_custom”, self.on_custom) self.subscribe_to_event(“mp_state_sync”, self.on_state_sync) self.subscribe_to_event(“mp_disconnected”, self.on_disconnected)

MODE_CLIENT = 'client'
MODE_HOST = 'host'
disconnect()[source]

Disconnect from the multiplayer session.

get_local_player() Player | None[source]

Get the local player.

get_player(player_id: str) Player | None[source]

Get a specific player by ID.

get_player_count() int[source]
get_players() list[Player][source]

Return list of all players in the room.

host_game(room_name: str = 'Room')[source]

Start hosting a multiplayer game. Sets up WebSocket server.

property is_active: bool
property is_connected: bool
property is_host: bool
join_game(url: str, player_name: str = None)[source]

Join a hosted game as a client.

kick_player(player_id: str)[source]

Host only: Kick a player from the room.

property local_player_id: str
on_destroy()[source]
poll()[source]

Process incoming WebSocket messages. Call this in on_update. Emits global events for game scripts to handle.

register_rpc(method_name: str, handler: callable)[source]

Register an RPC handler function.

request_despawn(entity_net_id: str)[source]

Host only: Despawn a networked entity.

request_spawn(prefab_path: str, owner_id: str = '', data: dict = None)[source]

Host only: Request spawning a networked entity.

property room: Room | None
rpc(method: str, data: dict = None)[source]

Send an RPC to all players (including self).

rpc_to(player_id: str, method: str, data: dict = None)[source]

Send an RPC to a specific player.

rpc_to_host(method: str, data: dict = None)[source]

Send an RPC to the host.

send_custom(channel: str, data: dict = None)[source]

Send a custom message to all players.

send_state(entity_net_id: str, state_data: dict)[source]

Send entity state data (used by NetworkIdentityComponent).

set_ready(ready: bool = True)[source]

Set local player’s ready state.

start_game()[source]

Host only: Start the game if all players are ready.

core.components.network_identity module

class core.components.network_identity.NetworkIdentityComponent(network_id: str = '', owner_id: str = '', sync_transform: bool = True, sync_interval: float = 0.05, interpolate: bool = True)[source]

Bases: Component

Marks an entity as network-synchronized across multiplayer sessions.

Attach to any entity that should be replicated. Works with MultiplayerComponent on a manager entity to sync transform, custom properties, and ownership.

Usage in scripts:

net_id = self.entity.get_component(NetworkIdentityComponent)

# Check ownership if net_id.is_mine():

# Only the owner should move this entity transform.x += speed * dt

# Set a synced variable (auto-replicated to all peers) net_id.set_var(“health”, 100) net_id.set_var(“score”, 42)

# Read synced variable health = net_id.get_var(“health”, default=100)

# Transfer ownership (host authority) net_id.transfer_ownership(new_player_id)

Auto-sync behavior:
  • Transform (x, y, rotation) is synced automatically based on sync_transform flag.

  • Custom variables set via set_var() are synced when changed.

  • Sync happens at the rate configured on the MultiplayerComponent.

get_all_vars() dict[source]

Get a copy of all synced variables.

get_var(key: str, default=None)[source]

Get a synced variable value.

is_mine() bool[source]

Check if the local player owns this entity.

receive_state(state_data: dict)[source]

Apply received state from the network.

set_var(key: str, value)[source]

Set a synced variable. Changes are replicated to all peers.

transfer_ownership(new_owner_id: str)[source]

Transfer ownership of this entity to another player (host authority).

update_sync(dt: float)[source]

Called each frame by the network system to handle sync logic.

core.components.particle_emitter module

class core.components.particle_emitter.ParticleEmitterComponent(emitting: bool = True, one_shot: bool = False, local_space: bool = False, render_layer: str = 'front', blend_additive: bool = False, max_particles: int = 512, emission_rate: float = 0.0, burst_count: int = 0, burst_interval: float = 1.0, lifetime_min: float = 0.25, lifetime_max: float = 0.75, speed_min: float = 30.0, speed_max: float = 90.0, direction_degrees: float = 270.0, spread_degrees: float = 360.0, gravity_x: float = 0.0, gravity_y: float = 0.0, damping: float = 0.0, radial_offset_min: float = 0.0, radial_offset_max: float = 0.0, angular_velocity_min: float = 0.0, angular_velocity_max: float = 0.0, start_size_min: float = 4.0, start_size_max: float = 10.0, end_size_min: float = 0.0, end_size_max: float = 2.0, start_color: tuple[int, int, int, int] = (255, 180, 80, 255), end_color: tuple[int, int, int, int] = (200, 60, 10, 0), emitter_lifetime: float = -1.0, shape: str = 'circle')[source]

Bases: Component

LAYER_BEHIND = 'behind'
LAYER_FRONT = 'front'
SHAPE_CIRCLE = 'circle'
SHAPE_PIXEL = 'pixel'
SHAPE_SQUARE = 'square'
classmethod explosion()[source]
classmethod magic()[source]
classmethod smoke()[source]
start(reset: bool = False)[source]
stop(clear_particles: bool = False)[source]
trigger_burst(count: int = 1)[source]

core.components.rigidbody module

class core.components.rigidbody.Rigidbody2D(velocity_x: float = 0.0, velocity_y: float = 0.0, mass: float = 1.0, angular_velocity: float = 0.0, gravity_scale: float = 1.0, use_gravity: bool = True, body_type: str = 'dynamic', is_kinematic: bool = False, restitution: float = 0.0, friction: float = 0.0, linear_damping: float = 0.0, angular_damping: float = 0.0, freeze_rotation: bool = False)[source]

Bases: Component

BODY_TYPE_DYNAMIC = 'dynamic'
BODY_TYPE_KINEMATIC = 'kinematic'
BODY_TYPE_STATIC = 'static'
apply_angular_impulse(angular_impulse: float)[source]
apply_force(force_x: float, force_y: float)[source]
apply_impulse(impulse_x: float, impulse_y: float)[source]
apply_torque(torque: float)[source]
property body_type
property can_rotate
clear_forces()[source]
property elasticity
property force_x
property force_y
property is_dynamic
property is_kinematic
property is_static
property torque
property velocity_x
property velocity_y

core.components.script module

class core.components.script.ScriptComponent(script_path: str = '', class_name: str = '')[source]

Bases: Component

Component for attaching Python scripts to entities.

Scripts automatically receive the following injected attributes: - entity: The entity this script is attached to - logger: A logger instance with the name “script.<ClassName>”

Example

class MyScript:
def on_start(self):

# logger is automatically available! self.logger.info(“Script started”) print(self.entity.name) # entity is also available

call_group(group_name: str, method_name: str, *args, **kwargs)[source]

Calls a method on all script components of entities in the specified group.

cancel_tweens(entity=None)[source]

Cancel tweens. If entity is given, only that entity’s tweens.

change_scene(scene_name: str)[source]
destroy()[source]
emit_global_event(event_name: str, *args, **kwargs)[source]

Emit an event globally to the World (queued, 1-frame latency).

emit_global_event_immediate(event_name: str, *args, **kwargs)[source]

Emit an event globally and dispatch it synchronously (zero latency).

emit_local_event(event_name: str, *args, **kwargs)[source]

Emit an event on this entity (queued, 1-frame latency).

emit_local_event_immediate(event_name: str, *args, **kwargs)[source]

Emit an event on this entity and dispatch it synchronously (zero latency).

find(name: str)[source]

Finds an entity by name in the current world.

get_children(name: str) list[source]

Returns the children list of an entity found by name.

hide()[source]
instantiate_prefab(prefab_path: str, parent=None, name: str = None, x: float = None, y: float = None, rotation: float = None, scale_x: float = None, scale_y: float = None)[source]
process_physics(enabled: bool)[source]
show()[source]
spawn_prefab(prefab_path: str, parent=None, name: str = None, x: float = None, y: float = None, rotation: float = None, scale_x: float = None, scale_y: float = None)[source]
start_coroutine(gen)[source]

Schedule a coroutine (generator) on this script’s coroutine manager.

stop_coroutines()[source]

Cancel all running coroutines on this script.

subscribe_to_event(event_name: str, callback, target_entity=None)[source]

Subscribe to an event. :param event_name: Name of the event. :param callback: Method to call. :param target_entity: If provided, subscribes to that entity’s event.

If None, subscribes to the global World event.

tick_coroutines(dt: float)[source]

Advance all coroutines by dt. Called by ScriptSystem each frame.

tick_tweens(dt: float)[source]

Advance all tweens by dt. Called by ScriptSystem each frame.

tween(entity, attr_path: str, target: float, start: float | None = None, duration: float = 1.0, easing=None, on_complete=None, loops: int = 0, yoyo: bool = False)[source]

Create a tween animation on an entity property.

unsubscribe_from_event(event_name: str, callback, target_entity=None)[source]

Unsubscribe from an event.

core.components.sound module

class core.components.sound.SoundComponent(file_path='', volume=1.0, loop=False, is_music=False, autoplay=False, spatialize=True, min_distance=0.0, max_distance=600.0, pan_distance=300.0)[source]

Bases: Component

apply_output()[source]
load() bool[source]

Loads the sound resource.

on_destroy()[source]
pause()[source]

Pauses playback.

play()[source]

Plays the sound or music.

set_spatial(attenuation: float, pan: float)[source]
set_volume(volume: float)[source]

Sets the volume (0.0 to 1.0).

stop()[source]

Stops playback.

unpause()[source]

Resumes playback.

core.components.sprite_renderer module

class core.components.sprite_renderer.SpriteRenderer(color=(255, 255, 255), width=50, height=50, image_path=None)[source]

Bases: Component

property height
load_image(path)[source]
property width

core.components.steering module

class core.components.steering.AlignmentBehavior(weight: float = 1.0, neighbor_radius: float = 100.0)[source]

Bases: Component

Steer to match the average heading of neighbours.

class core.components.steering.ArriveBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0, slow_radius: float = 100.0)[source]

Bases: Component

Steer towards a target, decelerating smoothly within slow_radius.

property target: Vector2
class core.components.steering.CohesionBehavior(weight: float = 1.0, neighbor_radius: float = 100.0)[source]

Bases: Component

Steer towards the average position of neighbours.

class core.components.steering.FleeBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0, panic_distance: float = 200.0)[source]

Bases: Component

Steer away from a target position.

property target: Vector2
class core.components.steering.SeekBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0)[source]

Bases: Component

Steer towards a target position.

property target: Vector2
class core.components.steering.SeparationBehavior(weight: float = 1.0, neighbor_radius: float = 50.0)[source]

Bases: Component

Steer to avoid crowding neighbours within neighbor_radius.

class core.components.steering.SteeringAgentComponent(max_speed: float = 150.0, max_force: float = 300.0, mass: float = 1.0, drag: float = 0.0)[source]

Bases: Component

Core steering agent. Accumulates forces from behaviour components and applies the resulting velocity to the entity’s Transform each frame.

property velocity: Vector2
class core.components.steering.WanderBehavior(weight: float = 1.0, circle_distance: float = 60.0, circle_radius: float = 30.0, angle_change: float = 30.0)[source]

Bases: Component

Produces a gentle, random meandering force.

core.components.tilemap module

class core.components.tilemap.TileLayer(name: 'str' = 'Layer', width: 'int' = 10, height: 'int' = 10, tiles: 'List[int]' = <factory>, visible: 'bool' = True, offset_x: 'int' = 0, offset_y: 'int' = 0)[source]

Bases: object

array_to_world(array_x: int, array_y: int) tuple[int, int][source]

Convert array indices to world coordinates

ensure_size(width: int, height: int)[source]
expand_to_include(world_x: int, world_y: int)[source]

Expand the tilemap to include the given world coordinate

get(x: int, y: int) int[source]
get_world(world_x: int, world_y: int) int[source]

Get tile value at world coordinates

height: int = 10
name: str = 'Layer'
offset_x: int = 0
offset_y: int = 0
resize_with_offset(new_width: int, new_height: int, new_offset_x: int, new_offset_y: int)[source]

Resize the tilemap with a new offset

set(x: int, y: int, value: int)[source]
set_world(world_x: int, world_y: int, value: int)[source]

Set tile value at world coordinates, expanding if necessary

tiles: List[int]
visible: bool = True
width: int = 10
world_to_array(world_x: int, world_y: int) tuple[int, int][source]

Convert world coordinates to array indices

class core.components.tilemap.TilemapComponent(map_width: int = 20, map_height: int = 15, tileset: Tileset | None = None, cell_width: int | None = None, cell_height: int | None = None, layers: List[TileLayer] | None = None)[source]

Bases: Component

Spritesheet-based tilemap.

Coordinates: - Tile coordinates are (0..width-1, 0..height-1) - World origin is the parent entity Transform center by convention; editor treats tilemap origin as top-left.

Rendering uses the entity Transform as an anchor, see render system for details.

ensure_layer_sizes()[source]
get_tileset_frames()[source]

Lazy cache of sliced tile images. Returns a list of pygame.Surface frames, index (tile_id - 1).

layers: List[TileLayer]
class core.components.tilemap.Tileset(image_path: 'str' = '', tile_width: 'int' = 32, tile_height: 'int' = 32, spacing: 'int' = 0, margin: 'int' = 0)[source]

Bases: object

image_path: str = ''
margin: int = 0
spacing: int = 0
tile_height: int = 32
tile_width: int = 32

core.components.timer module

class core.components.timer.TimerComponent(duration: float = 1.0, one_shot: bool = True, autostart: bool = False, callback=None)[source]

Bases: Component

A simple timer component for user scripts.

Usage from a script:

timer = self.entity.add_component(TimerComponent(
    duration=2.0,
    one_shot=True,
    autostart=True,
    callback=self.on_timer_done,
))

Or create and start later:

timer = self.entity.add_component(TimerComponent(duration=1.5))
timer.start()

The callback is invoked each time the timer expires. For repeating timers (one_shot=False), it fires every duration seconds.

property elapsed: float
property is_finished: bool
property is_running: bool
reset()[source]

Reset elapsed time to zero and stop.

start()[source]

(Re)start the timer from zero.

stop()[source]

Stop the timer without resetting elapsed time.

tick(dt: float)[source]
property time_left: float

core.components.transform module

class core.components.transform.Transform(x=0.0, y=0.0, rotation=0.0, scale_x=1.0, scale_y=1.0)[source]

Bases: Component

property position
rotate(d_rot: float)[source]
property rotation
scale(ds_x: float, ds_y: float)[source]
scale_by_vec(vec: Vector2)[source]
property scale_vec
property scale_x
property scale_y
translate(dx: float, dy: float)[source]
translate_vec(vec: Vector2)[source]
property x
property y

core.components.ui module

class core.components.ui.ButtonComponent(text='Button', width=100.0, height=40.0, normal_color=(100, 100, 100), hover_color=(150, 150, 150), pressed_color=(50, 50, 50), text_color=(255, 255, 255))[source]

Bases: UIComponent

class core.components.ui.CheckBoxComponent(checked=False, size=20.0, checked_color=(0, 200, 0), unchecked_color=(200, 200, 200))[source]

Bases: UIComponent

class core.components.ui.GridBoxContainerComponent(columns=2, spacing_x=5.0, spacing_y=5.0)[source]

Bases: UIComponent

class core.components.ui.HBoxContainerComponent(spacing=5.0)[source]

Bases: UIComponent

class core.components.ui.ImageRenderer(image_path=None, color=(255, 255, 255), width=50.0, height=50.0)[source]

Bases: UIComponent

load_image(path)[source]
class core.components.ui.ProgressBarComponent(value=0.5, min_value=0.0, max_value=1.0, width=200.0, height=20.0, bg_color=(100, 100, 100), fill_color=(0, 200, 0))[source]

Bases: UIComponent

class core.components.ui.SliderComponent(value=0.0, min_value=0.0, max_value=1.0, width=200.0, height=20.0, track_color=(100, 100, 100), handle_color=(200, 200, 200))[source]

Bases: UIComponent

class core.components.ui.TextInputComponent(text='', placeholder='Enter text...', width=200.0, height=30.0, bg_color=(255, 255, 255), text_color=(0, 0, 0))[source]

Bases: UIComponent

class core.components.ui.TextRenderer(text='Text', font_size=24, color=(255, 255, 255), font_path=None)[source]

Bases: UIComponent

class core.components.ui.UIComponent[source]

Bases: Component

Base class for all UI components

class core.components.ui.VBoxContainerComponent(spacing=5.0)[source]

Bases: UIComponent

core.components.webrtc module

class core.components.webrtc.WebRTCComponent(ice_servers: str = 'stun:stun.l.google.com:19302', data_channel_label: str = 'game', ordered: bool = True, max_retransmits: int = -1, autostart: bool = False, max_queue_size: int = 1024)[source]

Bases: Component

WebRTC component for peer-to-peer data channel communication.

Supports creating and joining peer connections with data channels for low-latency game networking. Uses aiortc under the hood. A signaling mechanism (e.g. WebSocket) is needed to exchange offers/answers/ICE candidates between peers.

Usage in scripts:

rtc = self.entity.get_component(WebRTCComponent)

# Create an offer (caller side) rtc.create_offer()

# Poll for signaling data to send to the remote peer for sender, msg in rtc.poll():

if sender == “local”:

# msg is a dict like {“type”: “offer”, “sdp”: “…”} or # {“type”: “candidate”, …} # Send this to the remote peer via your signaling channel ws.send_json(msg)

# On the remote side, set the remote description from received signaling rtc.set_remote_description(offer_dict) # triggers answer creation

# Feed ICE candidates from the remote peer rtc.add_ice_candidate(candidate_dict)

# Send data over the data channel rtc.send(“Hello peer!”) rtc.send_json({“action”: “move”, “x”: 10})

# Poll for incoming data channel messages for sender, msg in rtc.poll():

if sender == “datachannel”:

print(“Received:”, msg)

# Close rtc.close()

add_ice_candidate(candidate_dict: dict)[source]

Add an ICE candidate received from the signaling channel. candidate_dict should have keys like “candidate”, “sdpMid”, “sdpMLineIndex”.

close()[source]

Close the peer connection and stop the event loop.

create_answer()[source]

Create an SDP answer (callee side). Results appear in poll() as local signaling.

create_offer()[source]

Create an SDP offer (caller side). Results appear in poll() as local signaling.

is_connected() bool[source]

Return True if the data channel is open.

is_running() bool[source]

Return True if the WebRTC component is actively running.

on_destroy()[source]

Called when the entity is destroyed.

poll() list[tuple][source]

Drain the inbox and return a list of (sender, message) tuples. Call this in on_update.

Sender types:
  • “local”: signaling data to forward to the remote peer

    (offer, answer, or ICE candidate dicts)

  • “datachannel”: data received from the remote peer

  • “system”: connection state events like

    {“event”: “connected”}, {“event”: “disconnected”}, {“event”: “error”, “message”: “…”}

send(message: str)[source]

Send a string message over the data channel.

send_bytes(data: bytes)[source]

Send raw bytes over the data channel.

send_json(data)[source]

Send a JSON-serializable object as a string message.

set_remote_description(sdp_dict: dict)[source]

Set the remote SDP description received from the signaling channel. sdp_dict should have keys “type” (“offer” or “answer”) and “sdp”. If type is “offer”, an answer is automatically created.

start()[source]

Initialize the peer connection and background event loop.

core.components.websocket module

class core.components.websocket.WebSocketComponent(mode: str = 'client', host: str = 'localhost', port: int = 8765, url: str = '', autostart: bool = False, max_queue_size: int = 1024)[source]

Bases: Component

WebSocket component supporting both server and client modes.

Modes:
  • “server”: Listens on host:port, accepts client connections.

  • “client”: Connects to a remote WebSocket server.

Usage in scripts:

# Access the component ws = self.entity.get_component(WebSocketComponent)

# Start the connection (call once, e.g. in on_start) ws.start()

# In on_update, poll for incoming messages for sender, message in ws.poll():

print(f”Received: {message}”)

# Send data ws.send(“Hello”) # Client: send to server. Server: broadcast to all. ws.send_json({“key”: “val”}) # Send a JSON-serializable dict. ws.broadcast(“Hi everyone”) # Server only: broadcast to all clients. ws.send_to(client_id, “Hi”) # Server only: send to a specific client.

# Stop cleanly ws.stop()

MODE_CLIENT = 'client'
MODE_SERVER = 'server'
broadcast(message: str)[source]

Server only: Broadcast a message to all connected clients.

get_client_count() int[source]

Server only: Return the number of connected clients.

get_client_ids() list[int][source]

Server only: Return a list of connected client IDs.

get_url() str[source]

Return the effective WebSocket URL.

is_running() bool[source]

Return True if the WebSocket is actively running.

on_destroy()[source]

Called when the entity is destroyed.

poll() list[tuple][source]

Drain the inbox and return a list of (sender, message) tuples. Call this in on_update to process incoming messages on the game thread.

For server mode, sender is the client_id (int). For client mode, sender is “server”. Connection/disconnection events use sender “system” with message dicts like {“event”: “connected”, “client_id”: id} or {“event”: “disconnected”, “client_id”: id}.

send(message: str)[source]

Client mode: Send a message to the server. Server mode: Broadcast a message to all connected clients.

send_json(data)[source]

Send a JSON-serializable object as a string message.

send_to(client_id: int, message: str)[source]

Server only: Send a message to a specific client by ID.

start()[source]

Start the WebSocket server or client in a background thread.

stop()[source]

Stop the WebSocket server or client cleanly.

core.components.webview module

class core.components.webview.WebviewComponent(url: str = '', title: str = 'Webview', width: int = 800, height: int = 600, resizable: bool = True, frameless: bool = False, autoopen: bool = False)[source]

Bases: Component

Webview component that opens a native browser window using pywebview.

Supports loading a URL or raw HTML content. The webview runs in a background thread so it doesn’t block the game loop. Communication between the game and webview is done via message queues.

Usage in scripts:

wv = self.entity.get_component(WebviewComponent)

# Open a URL wv.open()

# Or open with specific URL wv.url = “https://example.com” wv.open()

# Load raw HTML wv.load_html(“<h1>Hello from engine!</h1>”)

# Evaluate JavaScript in the webview wv.evaluate_js(“document.title”)

# Poll for JS evaluation results in on_update for result in wv.poll():

print(“JS result:”, result)

# Close the webview wv.close()

close()[source]

Close the webview window.

evaluate_js(script: str)[source]

Evaluate JavaScript in the webview. Results are queued and retrieved via poll().

get_url() str[source]

Return the current URL of the webview (if available).

is_open() bool[source]

Return True if the webview window is currently open.

load_html(html: str)[source]

Load raw HTML content into the webview.

load_url(url: str)[source]

Navigate the webview to a new URL.

on_destroy()[source]

Called when the entity is destroyed.

open(url: str = None)[source]

Open the webview window. Optionally override the URL.

poll() list[source]

Drain the inbox and return a list of JS evaluation results. Call this in on_update.

set_title(title: str)[source]

Change the webview window title.

Module contents

class core.components.AlignmentBehavior(weight: float = 1.0, neighbor_radius: float = 100.0)[source]

Bases: Component

Steer to match the average heading of neighbours.

class core.components.AnimatorComponent(controller_path: str = None, play_on_start: bool = True, speed: float = 1.0)[source]

Bases: Component

consume_trigger(trigger_name: str)[source]
get_current_frame()[source]
keep_only_triggers(valid_triggers: set[str])[source]
load_controller(path: str, preserve_state: bool = False)[source]
pause()[source]
play(state_name: str, restart: bool = False)[source]
reload_controller_if_changed()[source]
resume()[source]
set_trigger(trigger_name: str)[source]
stop(reset: bool = False)[source]
class core.components.ArriveBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0, slow_radius: float = 100.0)[source]

Bases: Component

Steer towards a target, decelerating smoothly within slow_radius.

property target: Vector2
class core.components.BoxCollider2D(width: float = None, height: float = None, offset_x: float = 0.0, offset_y: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295, rotation: float = 0.0)[source]

Bases: Component

property offset_x
property offset_y
class core.components.ButtonComponent(text='Button', width=100.0, height=40.0, normal_color=(100, 100, 100), hover_color=(150, 150, 150), pressed_color=(50, 50, 50), text_color=(255, 255, 255))[source]

Bases: UIComponent

class core.components.CameraComponent(active: bool = True, zoom: float = 1.0, rotation: float = 0.0, viewport_x: float = 0.0, viewport_y: float = 0.0, viewport_width: float = 1.0, viewport_height: float = 1.0, priority: int = 0, follow_target_id: str = '', follow_rotation: bool = True)[source]

Bases: Component

DECAY_EXPONENTIAL = 'exponential'
DECAY_LINEAR = 'linear'
property is_shaking: bool
shake(intensity: float = 5.0, duration: float = 0.3, decay: str = 'linear')[source]

Start a camera shake effect.

Parameters:
  • intensity – Maximum pixel offset per axis.

  • duration – How long the shake lasts in seconds.

  • decay – Decay curve — "linear" or "exponential".

property shake_offset: tuple[float, float]

Current (x, y) shake offset in pixels.

update_shake(dt: float)[source]

Advance the shake timer and compute the current offset. Called by the render system each frame.

class core.components.CheckBoxComponent(checked=False, size=20.0, checked_color=(0, 200, 0), unchecked_color=(200, 200, 200))[source]

Bases: UIComponent

class core.components.CircleCollider2D(radius: float = None, offset_x: float = 0.0, offset_y: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295, rotation: float = 0.0)[source]

Bases: Component

property offset_x
property offset_y
class core.components.CohesionBehavior(weight: float = 1.0, neighbor_radius: float = 100.0)[source]

Bases: Component

Steer towards the average position of neighbours.

class core.components.FleeBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0, panic_distance: float = 200.0)[source]

Bases: Component

Steer away from a target position.

property target: Vector2
class core.components.GridBoxContainerComponent(columns=2, spacing_x=5.0, spacing_y=5.0)[source]

Bases: UIComponent

class core.components.HBoxContainerComponent(spacing=5.0)[source]

Bases: UIComponent

class core.components.HTTPClientComponent(base_url: str = '', default_headers: dict = None, timeout: float = 30.0, max_concurrent: int = 4)[source]

Bases: Component

Persistent HTTP client component for making async HTTP requests.

Uses a background thread pool to perform requests without blocking the game loop. Results are queued and retrieved via poll().

Usage in scripts:

http = self.entity.get_component(HTTPClientComponent)

# GET request http.get(”https://api.example.com/data”, tag=”fetch_data”)

# POST request with JSON body http.post(”https://api.example.com/submit”,

body={“key”: “value”}, tag=”submit”)

# In on_update, poll for completed responses for response in http.poll():

if response.ok:

data = response.json() print(f”[{response.tag}] Got: {data}”)

else:

print(f”[{response.tag}] Error: {response.error}”)

delete(url: str, headers: dict = None, tag: str = '')[source]

Perform an async DELETE request.

get(url: str, headers: dict = None, tag: str = '')[source]

Perform an async GET request.

get_pending_count() int[source]

Return number of in-flight requests.

on_destroy()[source]

Wait briefly for pending threads to finish.

patch(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async PATCH request.

poll() list[HTTPResponse][source]

Drain the response inbox. Call this in on_update. Returns a list of HTTPResponse objects for completed requests.

post(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async POST request. Body can be str, bytes, or dict (sent as JSON).

put(url: str, body=None, headers: dict = None, tag: str = '')[source]

Perform an async PUT request.

class core.components.HTTPRequestComponent(url: str = '', method: str = 'GET', request_body: str = '', content_type: str = 'application/json', timeout: float = 30.0, send_on_start: bool = False)[source]

Bases: Component

Lightweight single-request component. Configure in the inspector or script, fire it, and poll for the result.

Usage in scripts:

req = self.entity.get_component(HTTPRequestComponent)

# Configure and send req.url = “https://api.example.com/data” req.method = “GET” req.send()

# In on_update, check if done if req.is_done():

print(req.status_code, req.response_body) if req.ok:

data = req.json()

# Or send with body req.url = “https://api.example.com/submit” req.method = “POST” req.request_body = ‘{“key”: “value”}’ req.send()

METHOD_DELETE = 'DELETE'
METHOD_GET = 'GET'
METHOD_PATCH = 'PATCH'
METHOD_POST = 'POST'
METHOD_PUT = 'PUT'
error: str
headers: dict
is_done() bool[source]

Return True if the request has completed (success or error).

is_sending() bool[source]

Return True if a request is currently in flight.

json()[source]

Parse response_body as JSON. Returns None on failure.

property ok: bool

Return True if the last request was successful (2xx).

on_destroy()[source]
reset()[source]

Reset the response state for reuse.

response_body: str
response_headers: dict
send()[source]

Fire the HTTP request in a background thread.

status_code: int
class core.components.LightOccluder2D(shape: str = 'box', width: float = 50.0, height: float = 50.0, radius: float = 25.0, points: list | None = None, offset_x: float = 0.0, offset_y: float = 0.0, receive_light: bool = False, receive_shadow: bool = False, rotation: float = 0.0)[source]

Bases: Component

Light occluder that blocks light and casts shadows.

Supports three shape types: - "box" — axis-aligned rectangle (width × height) - "circle" — circle defined by radius - "polygon" — arbitrary convex/concave polygon (list of Vector2 points)

When added via the inspector with shape "box" or "circle", the size is automatically derived from the entity’s SpriteRenderer if present.

SHAPE_BOX = 'box'
SHAPE_CIRCLE = 'circle'
SHAPE_POLYGON = 'polygon'
property points
class core.components.MultiplayerComponent(player_name: str = 'Player', max_players: int = 8, sync_rate: float = 20.0, port: int = 8765)[source]

Bases: Component

High-level multiplayer manager component.

Attach to a single entity (e.g. a GameManager) to manage multiplayer sessions. Uses a WebSocketComponent on the same entity for transport. Provides lobby management, player tracking, RPCs, and state sync.

Modes:
  • “host”: Creates a WebSocket server, manages the room as authority.

  • “client”: Connects to a host’s WebSocket server.

Usage in scripts:

mp = self.entity.get_component(MultiplayerComponent)

# Host a game mp.host_game(“MyRoom”)

# Or join a game mp.join_game(“ws://192.168.1.10:8765”, “PlayerName”)

# In on_update, pump the network mp.poll()

# Check players for player in mp.get_players():

print(player.name, player.is_ready)

# Set ready mp.set_ready(True)

# Start game (host only, when all ready) mp.start_game()

# Send RPC to all players mp.rpc(“take_damage”, {“amount”: 10})

# Send RPC to specific player mp.rpc_to(player_id, “heal”, {“amount”: 5})

# Send RPC to host only mp.rpc_to_host(“request_spawn”, {“prefab”: “bullet”})

# Send custom data mp.send_custom(“chat”, {“text”: “Hello!”})

# Listen for events (via global event system) self.subscribe_to_event(“mp_player_joined”, self.on_player_joined) self.subscribe_to_event(“mp_player_left”, self.on_player_left) self.subscribe_to_event(“mp_game_started”, self.on_game_started) self.subscribe_to_event(“mp_rpc”, self.on_rpc) self.subscribe_to_event(“mp_custom”, self.on_custom) self.subscribe_to_event(“mp_state_sync”, self.on_state_sync) self.subscribe_to_event(“mp_disconnected”, self.on_disconnected)

MODE_CLIENT = 'client'
MODE_HOST = 'host'
disconnect()[source]

Disconnect from the multiplayer session.

get_local_player() Player | None[source]

Get the local player.

get_player(player_id: str) Player | None[source]

Get a specific player by ID.

get_player_count() int[source]
get_players() list[Player][source]

Return list of all players in the room.

host_game(room_name: str = 'Room')[source]

Start hosting a multiplayer game. Sets up WebSocket server.

property is_active: bool
property is_connected: bool
property is_host: bool
join_game(url: str, player_name: str = None)[source]

Join a hosted game as a client.

kick_player(player_id: str)[source]

Host only: Kick a player from the room.

property local_player_id: str
on_destroy()[source]
poll()[source]

Process incoming WebSocket messages. Call this in on_update. Emits global events for game scripts to handle.

register_rpc(method_name: str, handler: callable)[source]

Register an RPC handler function.

request_despawn(entity_net_id: str)[source]

Host only: Despawn a networked entity.

request_spawn(prefab_path: str, owner_id: str = '', data: dict = None)[source]

Host only: Request spawning a networked entity.

property room: Room | None
rpc(method: str, data: dict = None)[source]

Send an RPC to all players (including self).

rpc_to(player_id: str, method: str, data: dict = None)[source]

Send an RPC to a specific player.

rpc_to_host(method: str, data: dict = None)[source]

Send an RPC to the host.

send_custom(channel: str, data: dict = None)[source]

Send a custom message to all players.

send_state(entity_net_id: str, state_data: dict)[source]

Send entity state data (used by NetworkIdentityComponent).

set_ready(ready: bool = True)[source]

Set local player’s ready state.

start_game()[source]

Host only: Start the game if all players are ready.

class core.components.NetworkIdentityComponent(network_id: str = '', owner_id: str = '', sync_transform: bool = True, sync_interval: float = 0.05, interpolate: bool = True)[source]

Bases: Component

Marks an entity as network-synchronized across multiplayer sessions.

Attach to any entity that should be replicated. Works with MultiplayerComponent on a manager entity to sync transform, custom properties, and ownership.

Usage in scripts:

net_id = self.entity.get_component(NetworkIdentityComponent)

# Check ownership if net_id.is_mine():

# Only the owner should move this entity transform.x += speed * dt

# Set a synced variable (auto-replicated to all peers) net_id.set_var(“health”, 100) net_id.set_var(“score”, 42)

# Read synced variable health = net_id.get_var(“health”, default=100)

# Transfer ownership (host authority) net_id.transfer_ownership(new_player_id)

Auto-sync behavior:
  • Transform (x, y, rotation) is synced automatically based on sync_transform flag.

  • Custom variables set via set_var() are synced when changed.

  • Sync happens at the rate configured on the MultiplayerComponent.

get_all_vars() dict[source]

Get a copy of all synced variables.

get_var(key: str, default=None)[source]

Get a synced variable value.

is_mine() bool[source]

Check if the local player owns this entity.

receive_state(state_data: dict)[source]

Apply received state from the network.

set_var(key: str, value)[source]

Set a synced variable. Changes are replicated to all peers.

transfer_ownership(new_owner_id: str)[source]

Transfer ownership of this entity to another player (host authority).

update_sync(dt: float)[source]

Called each frame by the network system to handle sync logic.

class core.components.ParticleEmitterComponent(emitting: bool = True, one_shot: bool = False, local_space: bool = False, render_layer: str = 'front', blend_additive: bool = False, max_particles: int = 512, emission_rate: float = 0.0, burst_count: int = 0, burst_interval: float = 1.0, lifetime_min: float = 0.25, lifetime_max: float = 0.75, speed_min: float = 30.0, speed_max: float = 90.0, direction_degrees: float = 270.0, spread_degrees: float = 360.0, gravity_x: float = 0.0, gravity_y: float = 0.0, damping: float = 0.0, radial_offset_min: float = 0.0, radial_offset_max: float = 0.0, angular_velocity_min: float = 0.0, angular_velocity_max: float = 0.0, start_size_min: float = 4.0, start_size_max: float = 10.0, end_size_min: float = 0.0, end_size_max: float = 2.0, start_color: tuple[int, int, int, int] = (255, 180, 80, 255), end_color: tuple[int, int, int, int] = (200, 60, 10, 0), emitter_lifetime: float = -1.0, shape: str = 'circle')[source]

Bases: Component

LAYER_BEHIND = 'behind'
LAYER_FRONT = 'front'
SHAPE_CIRCLE = 'circle'
SHAPE_PIXEL = 'pixel'
SHAPE_SQUARE = 'square'
classmethod explosion()[source]
classmethod magic()[source]
classmethod smoke()[source]
start(reset: bool = False)[source]
stop(clear_particles: bool = False)[source]
trigger_burst(count: int = 1)[source]
class core.components.PointLight2D(color: tuple = (255, 255, 255), radius: float = 200.0, intensity: float = 1.0, falloff: float = 2.0)[source]

Bases: Component

Omnidirectional 2D point light.

class core.components.PolygonCollider2D(points: list | None = None, offset_x: float = 0.0, offset_y: float = 0.0, rotation: float = 0.0, is_trigger: bool = False, category_mask: int = 1, collision_mask: int = 4294967295)[source]

Bases: Component

property offset_x
property offset_y
property points
class core.components.ProgressBarComponent(value=0.5, min_value=0.0, max_value=1.0, width=200.0, height=20.0, bg_color=(100, 100, 100), fill_color=(0, 200, 0))[source]

Bases: UIComponent

class core.components.Rigidbody2D(velocity_x: float = 0.0, velocity_y: float = 0.0, mass: float = 1.0, angular_velocity: float = 0.0, gravity_scale: float = 1.0, use_gravity: bool = True, body_type: str = 'dynamic', is_kinematic: bool = False, restitution: float = 0.0, friction: float = 0.0, linear_damping: float = 0.0, angular_damping: float = 0.0, freeze_rotation: bool = False)[source]

Bases: Component

BODY_TYPE_DYNAMIC = 'dynamic'
BODY_TYPE_KINEMATIC = 'kinematic'
BODY_TYPE_STATIC = 'static'
apply_angular_impulse(angular_impulse: float)[source]
apply_force(force_x: float, force_y: float)[source]
apply_impulse(impulse_x: float, impulse_y: float)[source]
apply_torque(torque: float)[source]
property body_type
property can_rotate
clear_forces()[source]
property elasticity
property force_x
property force_y
property is_dynamic
property is_kinematic
property is_static
property torque
property velocity_x
property velocity_y
class core.components.ScriptComponent(script_path: str = '', class_name: str = '')[source]

Bases: Component

Component for attaching Python scripts to entities.

Scripts automatically receive the following injected attributes: - entity: The entity this script is attached to - logger: A logger instance with the name “script.<ClassName>”

Example

class MyScript:
def on_start(self):

# logger is automatically available! self.logger.info(“Script started”) print(self.entity.name) # entity is also available

call_group(group_name: str, method_name: str, *args, **kwargs)[source]

Calls a method on all script components of entities in the specified group.

cancel_tweens(entity=None)[source]

Cancel tweens. If entity is given, only that entity’s tweens.

change_scene(scene_name: str)[source]
destroy()[source]
emit_global_event(event_name: str, *args, **kwargs)[source]

Emit an event globally to the World (queued, 1-frame latency).

emit_global_event_immediate(event_name: str, *args, **kwargs)[source]

Emit an event globally and dispatch it synchronously (zero latency).

emit_local_event(event_name: str, *args, **kwargs)[source]

Emit an event on this entity (queued, 1-frame latency).

emit_local_event_immediate(event_name: str, *args, **kwargs)[source]

Emit an event on this entity and dispatch it synchronously (zero latency).

find(name: str)[source]

Finds an entity by name in the current world.

get_children(name: str) list[source]

Returns the children list of an entity found by name.

hide()[source]
instantiate_prefab(prefab_path: str, parent=None, name: str = None, x: float = None, y: float = None, rotation: float = None, scale_x: float = None, scale_y: float = None)[source]
process_physics(enabled: bool)[source]
show()[source]
spawn_prefab(prefab_path: str, parent=None, name: str = None, x: float = None, y: float = None, rotation: float = None, scale_x: float = None, scale_y: float = None)[source]
start_coroutine(gen)[source]

Schedule a coroutine (generator) on this script’s coroutine manager.

stop_coroutines()[source]

Cancel all running coroutines on this script.

subscribe_to_event(event_name: str, callback, target_entity=None)[source]

Subscribe to an event. :param event_name: Name of the event. :param callback: Method to call. :param target_entity: If provided, subscribes to that entity’s event.

If None, subscribes to the global World event.

tick_coroutines(dt: float)[source]

Advance all coroutines by dt. Called by ScriptSystem each frame.

tick_tweens(dt: float)[source]

Advance all tweens by dt. Called by ScriptSystem each frame.

tween(entity, attr_path: str, target: float, start: float | None = None, duration: float = 1.0, easing=None, on_complete=None, loops: int = 0, yoyo: bool = False)[source]

Create a tween animation on an entity property.

unsubscribe_from_event(event_name: str, callback, target_entity=None)[source]

Unsubscribe from an event.

class core.components.SeekBehavior(target_x: float = 0.0, target_y: float = 0.0, weight: float = 1.0)[source]

Bases: Component

Steer towards a target position.

property target: Vector2
class core.components.SeparationBehavior(weight: float = 1.0, neighbor_radius: float = 50.0)[source]

Bases: Component

Steer to avoid crowding neighbours within neighbor_radius.

class core.components.SliderComponent(value=0.0, min_value=0.0, max_value=1.0, width=200.0, height=20.0, track_color=(100, 100, 100), handle_color=(200, 200, 200))[source]

Bases: UIComponent

class core.components.SoundComponent(file_path='', volume=1.0, loop=False, is_music=False, autoplay=False, spatialize=True, min_distance=0.0, max_distance=600.0, pan_distance=300.0)[source]

Bases: Component

apply_output()[source]
load() bool[source]

Loads the sound resource.

on_destroy()[source]
pause()[source]

Pauses playback.

play()[source]

Plays the sound or music.

set_spatial(attenuation: float, pan: float)[source]
set_volume(volume: float)[source]

Sets the volume (0.0 to 1.0).

stop()[source]

Stops playback.

unpause()[source]

Resumes playback.

class core.components.SpotLight2D(color: tuple = (255, 255, 255), radius: float = 300.0, intensity: float = 1.0, falloff: float = 2.0, angle: float = 0.0, cone_angle: float = 45.0, offset_x: float = 0.0, offset_y: float = 0.0)[source]

Bases: Component

Directional 2D spot light with a cone angle.

class core.components.SpriteRenderer(color=(255, 255, 255), width=50, height=50, image_path=None)[source]

Bases: Component

property height
load_image(path)[source]
property width
class core.components.SteeringAgentComponent(max_speed: float = 150.0, max_force: float = 300.0, mass: float = 1.0, drag: float = 0.0)[source]

Bases: Component

Core steering agent. Accumulates forces from behaviour components and applies the resulting velocity to the entity’s Transform each frame.

property velocity: Vector2
class core.components.TextInputComponent(text='', placeholder='Enter text...', width=200.0, height=30.0, bg_color=(255, 255, 255), text_color=(0, 0, 0))[source]

Bases: UIComponent

class core.components.TextRenderer(text='Text', font_size=24, color=(255, 255, 255), font_path=None)[source]

Bases: UIComponent

class core.components.TileLayer(name: 'str' = 'Layer', width: 'int' = 10, height: 'int' = 10, tiles: 'List[int]' = <factory>, visible: 'bool' = True, offset_x: 'int' = 0, offset_y: 'int' = 0)[source]

Bases: object

array_to_world(array_x: int, array_y: int) tuple[int, int][source]

Convert array indices to world coordinates

ensure_size(width: int, height: int)[source]
expand_to_include(world_x: int, world_y: int)[source]

Expand the tilemap to include the given world coordinate

get(x: int, y: int) int[source]
get_world(world_x: int, world_y: int) int[source]

Get tile value at world coordinates

height: int = 10
name: str = 'Layer'
offset_x: int = 0
offset_y: int = 0
resize_with_offset(new_width: int, new_height: int, new_offset_x: int, new_offset_y: int)[source]

Resize the tilemap with a new offset

set(x: int, y: int, value: int)[source]
set_world(world_x: int, world_y: int, value: int)[source]

Set tile value at world coordinates, expanding if necessary

tiles: List[int]
visible: bool = True
width: int = 10
world_to_array(world_x: int, world_y: int) tuple[int, int][source]

Convert world coordinates to array indices

class core.components.TilemapComponent(map_width: int = 20, map_height: int = 15, tileset: Tileset | None = None, cell_width: int | None = None, cell_height: int | None = None, layers: List[TileLayer] | None = None)[source]

Bases: Component

Spritesheet-based tilemap.

Coordinates: - Tile coordinates are (0..width-1, 0..height-1) - World origin is the parent entity Transform center by convention; editor treats tilemap origin as top-left.

Rendering uses the entity Transform as an anchor, see render system for details.

ensure_layer_sizes()[source]
get_tileset_frames()[source]

Lazy cache of sliced tile images. Returns a list of pygame.Surface frames, index (tile_id - 1).

layers: List[TileLayer]
class core.components.Tileset(image_path: 'str' = '', tile_width: 'int' = 32, tile_height: 'int' = 32, spacing: 'int' = 0, margin: 'int' = 0)[source]

Bases: object

image_path: str = ''
margin: int = 0
spacing: int = 0
tile_height: int = 32
tile_width: int = 32
class core.components.TimerComponent(duration: float = 1.0, one_shot: bool = True, autostart: bool = False, callback=None)[source]

Bases: Component

A simple timer component for user scripts.

Usage from a script:

timer = self.entity.add_component(TimerComponent(
    duration=2.0,
    one_shot=True,
    autostart=True,
    callback=self.on_timer_done,
))

Or create and start later:

timer = self.entity.add_component(TimerComponent(duration=1.5))
timer.start()

The callback is invoked each time the timer expires. For repeating timers (one_shot=False), it fires every duration seconds.

property elapsed: float
property is_finished: bool
property is_running: bool
reset()[source]

Reset elapsed time to zero and stop.

start()[source]

(Re)start the timer from zero.

stop()[source]

Stop the timer without resetting elapsed time.

tick(dt: float)[source]
property time_left: float
class core.components.Transform(x=0.0, y=0.0, rotation=0.0, scale_x=1.0, scale_y=1.0)[source]

Bases: Component

property position
rotate(d_rot: float)[source]
property rotation
scale(ds_x: float, ds_y: float)[source]
scale_by_vec(vec: Vector2)[source]
property scale_vec
property scale_x
property scale_y
translate(dx: float, dy: float)[source]
translate_vec(vec: Vector2)[source]
property x
property y
core.components.UIImageRenderer

alias of ImageRenderer

class core.components.VBoxContainerComponent(spacing=5.0)[source]

Bases: UIComponent

class core.components.WanderBehavior(weight: float = 1.0, circle_distance: float = 60.0, circle_radius: float = 30.0, angle_change: float = 30.0)[source]

Bases: Component

Produces a gentle, random meandering force.

class core.components.WebRTCComponent(ice_servers: str = 'stun:stun.l.google.com:19302', data_channel_label: str = 'game', ordered: bool = True, max_retransmits: int = -1, autostart: bool = False, max_queue_size: int = 1024)[source]

Bases: Component

WebRTC component for peer-to-peer data channel communication.

Supports creating and joining peer connections with data channels for low-latency game networking. Uses aiortc under the hood. A signaling mechanism (e.g. WebSocket) is needed to exchange offers/answers/ICE candidates between peers.

Usage in scripts:

rtc = self.entity.get_component(WebRTCComponent)

# Create an offer (caller side) rtc.create_offer()

# Poll for signaling data to send to the remote peer for sender, msg in rtc.poll():

if sender == “local”:

# msg is a dict like {“type”: “offer”, “sdp”: “…”} or # {“type”: “candidate”, …} # Send this to the remote peer via your signaling channel ws.send_json(msg)

# On the remote side, set the remote description from received signaling rtc.set_remote_description(offer_dict) # triggers answer creation

# Feed ICE candidates from the remote peer rtc.add_ice_candidate(candidate_dict)

# Send data over the data channel rtc.send(“Hello peer!”) rtc.send_json({“action”: “move”, “x”: 10})

# Poll for incoming data channel messages for sender, msg in rtc.poll():

if sender == “datachannel”:

print(“Received:”, msg)

# Close rtc.close()

add_ice_candidate(candidate_dict: dict)[source]

Add an ICE candidate received from the signaling channel. candidate_dict should have keys like “candidate”, “sdpMid”, “sdpMLineIndex”.

close()[source]

Close the peer connection and stop the event loop.

create_answer()[source]

Create an SDP answer (callee side). Results appear in poll() as local signaling.

create_offer()[source]

Create an SDP offer (caller side). Results appear in poll() as local signaling.

is_connected() bool[source]

Return True if the data channel is open.

is_running() bool[source]

Return True if the WebRTC component is actively running.

on_destroy()[source]

Called when the entity is destroyed.

poll() list[tuple][source]

Drain the inbox and return a list of (sender, message) tuples. Call this in on_update.

Sender types:
  • “local”: signaling data to forward to the remote peer

    (offer, answer, or ICE candidate dicts)

  • “datachannel”: data received from the remote peer

  • “system”: connection state events like

    {“event”: “connected”}, {“event”: “disconnected”}, {“event”: “error”, “message”: “…”}

send(message: str)[source]

Send a string message over the data channel.

send_bytes(data: bytes)[source]

Send raw bytes over the data channel.

send_json(data)[source]

Send a JSON-serializable object as a string message.

set_remote_description(sdp_dict: dict)[source]

Set the remote SDP description received from the signaling channel. sdp_dict should have keys “type” (“offer” or “answer”) and “sdp”. If type is “offer”, an answer is automatically created.

start()[source]

Initialize the peer connection and background event loop.

class core.components.WebSocketComponent(mode: str = 'client', host: str = 'localhost', port: int = 8765, url: str = '', autostart: bool = False, max_queue_size: int = 1024)[source]

Bases: Component

WebSocket component supporting both server and client modes.

Modes:
  • “server”: Listens on host:port, accepts client connections.

  • “client”: Connects to a remote WebSocket server.

Usage in scripts:

# Access the component ws = self.entity.get_component(WebSocketComponent)

# Start the connection (call once, e.g. in on_start) ws.start()

# In on_update, poll for incoming messages for sender, message in ws.poll():

print(f”Received: {message}”)

# Send data ws.send(“Hello”) # Client: send to server. Server: broadcast to all. ws.send_json({“key”: “val”}) # Send a JSON-serializable dict. ws.broadcast(“Hi everyone”) # Server only: broadcast to all clients. ws.send_to(client_id, “Hi”) # Server only: send to a specific client.

# Stop cleanly ws.stop()

MODE_CLIENT = 'client'
MODE_SERVER = 'server'
broadcast(message: str)[source]

Server only: Broadcast a message to all connected clients.

get_client_count() int[source]

Server only: Return the number of connected clients.

get_client_ids() list[int][source]

Server only: Return a list of connected client IDs.

get_url() str[source]

Return the effective WebSocket URL.

is_running() bool[source]

Return True if the WebSocket is actively running.

on_destroy()[source]

Called when the entity is destroyed.

poll() list[tuple][source]

Drain the inbox and return a list of (sender, message) tuples. Call this in on_update to process incoming messages on the game thread.

For server mode, sender is the client_id (int). For client mode, sender is “server”. Connection/disconnection events use sender “system” with message dicts like {“event”: “connected”, “client_id”: id} or {“event”: “disconnected”, “client_id”: id}.

send(message: str)[source]

Client mode: Send a message to the server. Server mode: Broadcast a message to all connected clients.

send_json(data)[source]

Send a JSON-serializable object as a string message.

send_to(client_id: int, message: str)[source]

Server only: Send a message to a specific client by ID.

start()[source]

Start the WebSocket server or client in a background thread.

stop()[source]

Stop the WebSocket server or client cleanly.

class core.components.WebviewComponent(url: str = '', title: str = 'Webview', width: int = 800, height: int = 600, resizable: bool = True, frameless: bool = False, autoopen: bool = False)[source]

Bases: Component

Webview component that opens a native browser window using pywebview.

Supports loading a URL or raw HTML content. The webview runs in a background thread so it doesn’t block the game loop. Communication between the game and webview is done via message queues.

Usage in scripts:

wv = self.entity.get_component(WebviewComponent)

# Open a URL wv.open()

# Or open with specific URL wv.url = “https://example.com” wv.open()

# Load raw HTML wv.load_html(“<h1>Hello from engine!</h1>”)

# Evaluate JavaScript in the webview wv.evaluate_js(“document.title”)

# Poll for JS evaluation results in on_update for result in wv.poll():

print(“JS result:”, result)

# Close the webview wv.close()

close()[source]

Close the webview window.

evaluate_js(script: str)[source]

Evaluate JavaScript in the webview. Results are queued and retrieved via poll().

get_url() str[source]

Return the current URL of the webview (if available).

is_open() bool[source]

Return True if the webview window is currently open.

load_html(html: str)[source]

Load raw HTML content into the webview.

load_url(url: str)[source]

Navigate the webview to a new URL.

on_destroy()[source]

Called when the entity is destroyed.

open(url: str = None)[source]

Open the webview window. Optionally override the URL.

poll() list[source]

Drain the inbox and return a list of JS evaluation results. Call this in on_update.

set_title(title: str)[source]

Change the webview window title.