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.rsSystem 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:
- State-Based Spawning: Use
OnEnterschedule to spawn player when entering gameplay states - Multiple States: Register for both
TutorialandPlayingto handle different entry points - 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 entitySpatialBundle: ProvidesTransform,GlobalTransform,Visibility, andInheritedVisibilityName: Entity name for debugging ("Hero")
Gameplay Components
Skills: Player skill levels and XP (Attack, Strength, Defense, etc.)Inventory: 28-slot inventory for itemsEquipment: Equipped items (weapon, armor, etc.)Bank: Bank storage for itemsPrayerPoints: Prayer system state
Movement Components
MovementTarget: Target position for movement systemPathAgent: Pathfinding agent for navigation
Why This Pattern Works
1. State-Based Initialization
Using
OnEnter ensures the player is spawned exactly when needed:- Entering
TutorialfromMainMenu→ spawn player - Entering
PlayingfromMainMenu(Continue) → spawn player - Entering
PlayingfromTutorial(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_componentssystem can add any missing components later
Integration with Existing Systems
Camera System
File:
src/systems/camera/mod.rsThe 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.rsThe
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.rsThe
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
- Spawn Position: Currently hardcoded to
(0.0, 0.5, 0.0). Should be configurable or loaded from save file. - Component Defaults: Some components use
::default()which might not be appropriate (e.g.,PrayerPointsshould be based on Prayer level). - Save File Integration: When loading a save file, should restore player position and state instead of spawning at origin.
- Tutorial-Specific Spawn: Tutorial might want a different spawn position or initial equipment.