spawn player implementation

Guide for spawn player implementation

Player Spawning Implementation

Solution Overview

Implemented a state-based player spawning system that creates the player entity when entering gameplay states (Tutorial or Playing).

Implementation

File: src/systems/player/mod.rs

System Registration

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<CombatConfig>()
           .add_plugins((
               inventory::InventoryPlugin,
               equipment::EquipmentPlugin,
               interactions::InteractionPlugin,
           ))
           .add_plugins(skills::SkillsPlugin)
           .add_plugins(progression::MilestonePlugin)
           .register_type::<Luck>()
           .add_systems(OnEnter(crate::GameState::Tutorial), spawn_player) // [FIX]
           .add_systems(OnEnter(crate::GameState::Playing), spawn_player)  // [FIX]
           .add_systems(Update, (
               ensure_player_components,
               combat_interaction::combat_input,
               equipment::update_equipment_stats,
           ));
    }
}
Key Design Decisions:
  1. State-Based Spawning: Use OnEnter schedule to spawn player when entering gameplay states
  2. Multiple States: Register for both Tutorial and Playing to handle different entry points
  3. Idempotent: Check if player already exists before spawning to prevent duplicates

Spawn Function

// [FIX] Spawn Player Entity
pub fn spawn_player(
    mut commands: Commands,
    query: Query<Entity, With<Player>>,
) {
    if !query.is_empty() {
        return; // Player already exists
    }

    info!("Spawning Player Entity...");
    
    commands.spawn((
        Player,
        SpatialBundle {
            transform: Transform::from_xyz(0.0, 0.5, 0.0), // Start slightly above ground
            ..default()
        },
        // Core Components
        crate::systems::player::skills::Skills::default(),
        crate::systems::player::inventory::Inventory::new(28),
        crate::systems::player::inventory::Equipment::default(),
        crate::systems::player::bank::Bank::default(),
        crate::systems::player::prayer::PrayerPoints::default(), // ❌ Compilation error
        
        // Movement
        MovementTarget(Vec3::new(0.0, 0.5, 0.0)),
        crate::systems::ai::pathfinding::PathAgent::default(), // ❌ Compilation error
        
        // State
        Name::new("Hero"),
    ));
}

Component Initialization

The spawned player entity includes:

Required Components

  • Player: Marker component identifying this as the player entity
  • SpatialBundle: Provides Transform, GlobalTransform, Visibility, and InheritedVisibility
  • Name: Entity name for debugging ("Hero")

Gameplay Components

  • Skills: Player skill levels and XP (Attack, Strength, Defense, etc.)
  • Inventory: 28-slot inventory for items
  • Equipment: Equipped items (weapon, armor, etc.)
  • Bank: Bank storage for items
  • PrayerPoints: Prayer system state

Movement Components

  • MovementTarget: Target position for movement system
  • PathAgent: Pathfinding agent for navigation

Why This Pattern Works

1. State-Based Initialization

Using OnEnter ensures the player is spawned exactly when needed:
  • Entering Tutorial from MainMenu → spawn player
  • Entering Playing from MainMenu (Continue) → spawn player
  • Entering Playing from Tutorial (tutorial complete) → player already exists (idempotent check)

2. Idempotent Design

The if !query.is_empty() check prevents duplicate player entities:
if !query.is_empty() {
    return; // Player already exists
}
This is crucial because:
  • State transitions might occur multiple times
  • Loading a save file might spawn the player separately
  • Multiple entry points to gameplay states exist

3. Component Bundle

Spawning all required components together ensures:
  • Player is fully initialized in one frame
  • No systems fail due to missing components
  • ensure_player_components system can add any missing components later

Integration with Existing Systems

Camera System

File: src/systems/camera/mod.rs
The camera system queries for the player:
player_query: Query<(Entity, &Transform), (With<crate::systems::player::Player>, Without<MainCamera>)>
Once the player is spawned, the camera will automatically follow it.

World Visuals

File: src/systems/world/mod.rs
The ensure_player_visuals system adds a visual mesh to the player:
fn ensure_player_visuals(
    mut commands: Commands,
    player_query: Query<(Entity, &GlobalTransform), (With<crate::systems::player::Player>, Without<Handle<Mesh>>)>,
    // ...
) {
    for (entity, _) in player_query.iter() {
        commands.entity(entity).insert(PbrBundle {
            mesh: meshes.add(Cylinder::new(0.5, 1.8)),
            material: materials.add(StandardMaterial {
                base_color: Color::srgb(0.2, 0.5, 0.9), // Player Blue
                // ...
            }),
            // ...
        });
    }
}
This system runs on Update, so it will add the visual mesh after the player is spawned.

Component Initialization

File: src/systems/player/mod.rs
The ensure_player_components system adds any missing components:
pub fn ensure_player_components(
    mut commands: Commands,
    query: Query<Entity, (With<Player>, Without<crate::systems::combat::CombatBonuses>)>,
    // ...
) {
    for entity in query.iter() {
        commands.entity(entity).insert(crate::systems::combat::CombatBonuses::default());
        info!("Attached CombatBonuses to Player");
    }
    // ... other components ...
}
This provides a safety net for any components not included in the initial spawn.

Known Issues

See compilation_errors.md for current compilation errors that need fixing.

Future Improvements

  1. Spawn Position: Currently hardcoded to (0.0, 0.5, 0.0). Should be configurable or loaded from save file.
  2. Component Defaults: Some components use ::default() which might not be appropriate (e.g., PrayerPoints should be based on Prayer level).
  3. Save File Integration: When loading a save file, should restore player position and state instead of spawning at origin.
  4. Tutorial-Specific Spawn: Tutorial might want a different spawn position or initial equipment.