Skip to content

PlayerPlugin

Manages player spawning, movement, animation, and state.

Location

Configuration

The PlayerPlugin uses the following settings from pedre.conf.settings:

Movement Settings

  • PLAYER_MOVEMENT_SPEED - Movement speed in pixels per second (default: 180.0)
  • TILE_SIZE - Size of tiles for grid-based calculations (default: 64)

These can be overridden in your project's settings.py:

# Custom player settings
PLAYER_MOVEMENT_SPEED = 200.0
TILE_SIZE = 32

Public API

Player Access

get_player_sprite

get_player_sprite() -> AnimatedSprite | None

Get the player sprite instance.

Returns:

  • The AnimatedSprite instance or None if not loaded

Example:

player_sprite = context.player_plugin.get_player_sprite()
if player_sprite:
    print(f"Player at ({player_sprite.center_x}, {player_sprite.center_y})")

Notes:

  • Returns None before scene is loaded
  • Player sprite is created during load_from_tiled()
  • Used by other plugins (camera, portals, interactions)

Plugin Lifecycle

setup

setup(context: GameContext) -> None

Initialize the player plugin with game context.

Parameters:

  • context - Game context providing access to other plugins

Notes:

  • Called automatically by PluginLoader
  • Stores reference to game context
  • Must be called before loading player from Tiled

update

update(delta_time: float) -> None

Update player movement and animation.

Parameters:

  • delta_time - Time since last update in seconds

Example:

def on_update(self, delta_time):
    self.player_plugin.update(delta_time)

Notes:

  • Called automatically by PluginLoader each frame
  • Processes input from InputPlugin
  • Blocks movement when dialog is showing
  • Updates player position and animation state
  • Handles direction changes based on movement

load_from_tiled

load_from_tiled(tile_map: arcade.TileMap, arcade_scene: arcade.Scene) -> None

Load player from Tiled map object layer.

Parameters:

  • tile_map - The loaded Tiled map
  • arcade_scene - The arcade Scene to add player to

Notes:

  • Called automatically by PluginLoader
  • Looks for "Player" object layer
  • Uses first player object in layer
  • Resolves sprite via content registry (sprites sub-registry, using sprite_id property or "player" as default)
  • Supports portal-based spawning via waypoints

reset

reset() -> None

Reset player plugin state for new game.

Notes:

  • Clears player sprite and sprite list
  • Called when starting a new game

Save/Load Support

get_save_state

get_save_state() -> dict[str, Any]

Return serializable state for saving.

Returns:

  • Dictionary containing player position

Example:

save_data = {
    "player": player_plugin.get_save_state(),
    # ... other save data
}

Notes:

  • Saves player_x and player_y coordinates
  • Returns empty dict if no player sprite exists

restore_save_state

restore_save_state(state: dict[str, Any]) -> None

Phase 1: No metadata to restore for player (sprites don't exist yet).

Parameters:

  • state - Dictionary containing saved player state

Notes:

  • Player sprite doesn't exist during this phase
  • Actual restoration happens in apply_entity_state()

apply_entity_state

apply_entity_state(state: dict[str, Any]) -> None

Phase 2: Apply saved player position after sprite exists.

Parameters:

  • state - Dictionary containing saved player state

Example:

player_plugin.apply_entity_state(save_data["player"])

Notes:

  • Restores player_x and player_y coordinates
  • Must be called after load_from_tiled()
  • Logs restored position for debugging

State Serialization

to_dict

to_dict() -> dict[str, float]

Serialize player position to dictionary.

Returns:

  • Dictionary with player_x and player_y keys

Example:

position_data = player_plugin.to_dict()
print(f"Player at ({position_data['player_x']}, {position_data['player_y']})")

from_dict

from_dict(data: dict[str, float]) -> None

Restore player position from dictionary.

Parameters:

  • data - Dictionary with player_x and player_y keys

Example:

player_plugin.from_dict({"player_x": 320.0, "player_y": 240.0})

Notes:

  • Only applies position if player sprite exists
  • Requires both player_x and player_y keys

Player Sprite and Content Registry

The player sprite is defined entirely via the content registry — no properties are read from the Tiled Player object (only its spawn position). The plugin looks up the "player" key in the players sub-registry (players.json), then uses that entry's sprite_id field to find the sprite definition in sprites.json.

Tiled Map Integration

The player is placed in Tiled maps using the "Player" object layer:

  1. Create a "Player" object layer
  2. Add a point object where the player should spawn by default

No Tiled object properties are needed. All sprite and spawn configuration lives in players.json.

players.json fields:

Field Type Required Description
sprite_id string Yes ID of the sprite definition in sprites.json
spawn_at_position list of strings No Map names where the player spawns at the Tiled object position instead of a portal waypoint

Example players.json:

{
  "player": {
    "sprite_id": "hero",
    "spawn_at_position": ["village"]
  }
}

Example sprite content registry entry (sprites.json):

{
  "player": {
    "sprite_sheet": "characters/player.png",
    "frame_width": 32,
    "states": {
      "idle": {"row": 0, "frames": 4},
      "walk": {"row": 1, "frames": 6}
    }
  }
}

See the Sprites Content Reference and Players Content Reference for full details on defining sprite and player entries.

Portal-Based Spawning

When loading a scene, the player can spawn at a waypoint instead of the default position:

How It Works:

  1. ScenePlugin stores a next_spawn_waypoint when transitioning scenes
  2. PlayerPlugin checks spawn_at_position in players.json for the current map name
  3. If the map is not in spawn_at_position and a waypoint exists, player spawns at the waypoint's pixel coordinates
  4. If the map is in spawn_at_position or no waypoint is set, uses the default position from the Tiled object

Example:

# Request scene transition with spawn waypoint
context.scene_plugin.request_transition(
    map_file="castle.tmx",
    spawn_waypoint="entrance"
)

# PlayerPlugin will spawn player at "entrance" waypoint
# instead of default position from Tiled map

Notes:

  • Waypoint must exist in target scene's waypoint plugin
  • Spawn waypoint is cleared after use (one-time)
  • Default position from Tiled is used as fallback

Player State

The PlayerPlugin maintains the player sprite and its state throughout the game session.

Player Sprite Lifecycle

  1. Creation - Player sprite is created during load_from_tiled()
  2. Updates - Position and animation updated every frame in update()
  3. Persistence - Position saved/restored during save/load operations
  4. Reset - Cleared when starting a new game

State Tracking

The player state includes:

  • Position - center_x and center_y coordinates in pixels
  • Direction - Current facing direction (up, down, left, right)
  • Animation State - Walking or idle
  • Velocity - Movement vector from input plugin

Movement Plugin

The PlayerPlugin handles movement processing each frame:

Movement Processing Flow

  1. Input Check - Get movement vector from InputPlugin
  2. Dialog Blocking - Block movement if dialog is showing
  3. Direction Update - Update player direction based on movement
  4. Animation Update - Update walk/idle animation state

Direction Logic

Direction changes are based on movement priority:

if dx > 0:
    direction = "right"
elif dx < 0:
    direction = "left"
elif dy > 0:
    direction = "up"
elif dy < 0:
    direction = "down"

Notes:

  • Horizontal movement (dx) takes precedence over vertical (dy)
  • Direction only updates when actually moving
  • Last direction is preserved when stopped

Animation States

The player has two animation states:

  • Idle - When stationary (dx = 0 and dy = 0)
  • Walk - When moving (dx != 0 or dy != 0)

Each state has 4 directional variants (up, down, left, right).

Usage Examples

Accessing Player Position

# Get player sprite
player_sprite = context.player_plugin.get_player_sprite()

if player_sprite:
    # Access position
    x = player_sprite.center_x
    y = player_sprite.center_y
    print(f"Player at ({x}, {y})")

Checking Player Distance

# Check distance to NPC
player_sprite = context.player_plugin.get_player_sprite()
npc_sprite = context.npc_plugin.get_npc_by_name("merchant").sprite

distance = arcade.get_distance_between_sprites(player_sprite, npc_sprite)
if distance < 50:
    print("Player near merchant")

Following Player with Camera

# Camera automatically follows player
context.camera_plugin.set_follow_player(smooth=True)

# Stop following
context.camera_plugin.stop_follow()

Spawning at Specific Location

The player spawns at waypoints during scene transitions. This is typically handled by scripts:

{
    "name": "change_scene",
    "target_map": "castle.tmx",
    "spawn_waypoint": "main_entrance"
}

Integration with Other Plugins

InputPlugin Integration

The InputPlugin provides movement vectors:

# In update loop
dx, dy = context.input_plugin.get_movement_vector()

# PlayerPlugin processes this input
# Updates player sprite velocity and direction

Notes:

  • Input is automatically processed during update()
  • Movement is blocked when dialogs are showing
  • Direction changes are based on movement vector

CameraPlugin Integration

The CameraPlugin can follow the player:

# Enable player following
context.camera_plugin.set_follow_player(smooth=True)

# Camera automatically tracks player position

Notes:

  • Player sprite must exist before camera can follow
  • Camera uses player position for centering
  • Smooth following interpolates camera movement

DialogPlugin Integration

Dialog blocks player movement:

# During update
if context.dialog_plugin.is_showing():
    # Player movement is blocked
    return

Notes:

  • Player cannot move while dialog is visible
  • Input is still processed but not applied
  • Movement resumes when dialog closes

ScenePlugin Integration

Scene transitions handle player spawning:

# ScenePlugin provides spawn waypoint
spawn_waypoint = context.scene_plugin.get_next_spawn_waypoint()

# PlayerPlugin spawns at waypoint location (pixel coordinates)
if spawn_waypoint:
    waypoint_pos = context.waypoint_plugin.get_waypoint(spawn_waypoint)
    if waypoint_pos:
        pixel_x, pixel_y = waypoint_pos
        player.center_x = pixel_x
        player.center_y = pixel_y

Notes:

  • Waypoint-based spawning is optional (spawn_at_portal property)
  • Falls back to Tiled map position if waypoint not found
  • Spawn waypoint is cleared after use

PhysicsPlugin Integration

Physics engine prevents wall collisions:

# Physics engine uses player sprite
physics_engine = arcade.PhysicsEngineSimple(player_sprite, walls)

# Movement is constrained by physics
physics_engine.update()

Notes:

  • Physics must be invalidated when player sprite changes
  • Collision response is handled by physics plugin
  • Player velocity is set by PlayerPlugin, constrained by PhysicsPlugin

Troubleshooting

Player Not Appearing

If the player doesn't appear on the map:

  1. Check Player layer - Ensure Tiled map has "Player" object layer
  2. Verify content registry - Ensure a "player" entry (or the value of sprite_id) exists in your sprites content registry
  3. Check spawn position - Verify player object has valid x/y coordinates
  4. Review logs - Look for errors during load_from_tiled()

Player Not Moving

If the player sprite exists but doesn't move:

  1. Check InputPlugin - Ensure input plugin is working (get_movement_vector())
  2. Verify update loop - Confirm player_plugin.update(delta_time) is called every frame
  3. Check dialog state - Ensure dialog isn't blocking movement
  4. Test physics - Verify physics engine allows movement (not stuck in walls)

Animation Not Playing

If animations aren't working:

  1. Check sprite definition - Ensure animation states are defined in the content registry sprite entry
  2. Verify sprite sheet - Ensure the sprite sheet file exists at the configured path
  3. Test movement - Animations require actual movement to trigger walk states
  4. Review AnimatedSprite - Check base animation plugin is functioning

Spawn Waypoint Issues

If portal-based spawning doesn't work:

  1. Check waypoint exists - Verify target waypoint is in destination scene
  2. Verify spawn_at_portal - Ensure property is set to true
  3. Check scene transition - Confirm spawn_waypoint is passed to scene change
  4. Review logs - Look for waypoint resolution warnings

Custom Player Implementation

If you need to replace the player plugin with a custom implementation, you can extend the PlayerBasePlugin abstract base class.

PlayerBasePlugin

Location: src/pedre/plugins/player/base.py

The PlayerBasePlugin class defines the minimum interface that any player plugin must implement.

Required Methods

Your custom player plugin must implement these abstract methods:

from pedre.plugins.player.base import PlayerBasePlugin
from pedre.sprites import AnimatedSprite

class CustomPlayerPlugin(PlayerBasePlugin):
    """Custom player implementation."""

    name = "player"
    dependencies = ["input", "waypoint"]

    def get_player_sprite(self) -> AnimatedSprite | None:
        """Get the player sprite."""
        ...

Registration

Register your custom player plugin using the @PluginRegistry.register decorator:

from pedre.plugins.registry import PluginRegistry
from pedre.plugins.player.base import PlayerBasePlugin

@PluginRegistry.register
class CustomPlayerPlugin(PlayerBasePlugin):
    name = "player"
    dependencies = ["input", "waypoint"]

    # ... implement all abstract methods ...

Notes on Custom Implementation

  • Your custom plugin inherits from BasePlugin (via PlayerBasePlugin), so you must implement the standard plugin lifecycle methods: setup(), cleanup(), and reset()
  • The role attribute is set to "player_plugin" in the base class
  • Your implementation can use any sprite type or movement system
  • Register your custom player plugin in your project's INSTALLED_PLUGINS setting before the default "pedre.plugins.player" to replace it

Example Custom Implementation:

# In myproject/plugins/custom_player.py
from pedre.plugins.registry import PluginRegistry
from pedre.plugins.player.base import PlayerBasePlugin

@PluginRegistry.register
class PhysicsPlayerPlugin(PlayerBasePlugin):
    """Player plugin with physics-based movement."""

    name = "player"
    dependencies = ["input", "waypoint", "physics"]

    def __init__(self):
        self.player_sprite = None
        self.velocity = (0, 0)
        # ... rest of initialization ...

    def get_player_sprite(self) -> AnimatedSprite | None:
        return self.player_sprite

    def update(self, delta_time: float) -> None:
        # Custom physics-based movement
        # ... physics calculations ...
        pass

    # ... implement other abstract methods ...
# In myproject/settings.py
INSTALLED_PLUGINS = [
    "myproject.plugins.custom_player",  # Load custom player first
    "pedre.plugins.camera",
    "pedre.plugins.audio",
    # ... rest of plugins (omit "pedre.plugins.player") ...
]

See Also