~starkingdoms/starkingdoms

48c6475b2c83e62ef93be47f4f45fe7a6ae3678c — ghostly_zsh 5 months ago 42f762d
player input
M crates/unified/assets/config/world.wc.toml => crates/unified/assets/config/world.wc.toml +4 -1
@@ 4,4 4,7 @@ gravity = 2
[part]
default_height = 50
default_width = 50
default_mass = 100
\ No newline at end of file
default_mass = 100

[hearty]
thrust = 50000

M crates/unified/src/client/incoming_parts.rs => crates/unified/src/client/incoming_parts.rs +1 -1
@@ 41,4 41,4 @@ fn handle_updated_parts(mut commands: Commands, mut updated_parts: Query<(Entity
            }));
        info!(?updated_part, "updated part");
    }
}
\ No newline at end of file
}

A crates/unified/src/client/key_input.rs => crates/unified/src/client/key_input.rs +36 -0
@@ 0,0 1,36 @@
use bevy::{app::{App, Update}, ecs::{event::EventWriter, system::Res}, input::{keyboard::KeyCode, ButtonInput}};

use crate::ecs::ThrustEvent;

pub fn key_input_plugin(app: &mut App) {
    app.add_systems(Update, directional_keys);
}

pub fn directional_keys(
    keys: Res<ButtonInput<KeyCode>>,
    mut thrust_event: EventWriter<ThrustEvent>,
) {
    if keys.just_pressed(KeyCode::KeyW) || keys.just_pressed(KeyCode::ArrowUp) {
        thrust_event.write(ThrustEvent::Up(true));
    } else if keys.just_released(KeyCode::KeyW) || keys.just_released(KeyCode::ArrowUp) {
        thrust_event.write(ThrustEvent::Up(false));
    }

    if keys.just_pressed(KeyCode::KeyS) || keys.just_pressed(KeyCode::ArrowDown) {
        thrust_event.write(ThrustEvent::Down(true));
    } else if keys.just_released(KeyCode::KeyS) || keys.just_released(KeyCode::ArrowDown) {
        thrust_event.write(ThrustEvent::Down(false));
    }

    if keys.just_pressed(KeyCode::KeyA) || keys.just_pressed(KeyCode::ArrowLeft) {
        thrust_event.write(ThrustEvent::Left(true));
    } else if keys.just_released(KeyCode::KeyA) || keys.just_released(KeyCode::ArrowLeft) {
        thrust_event.write(ThrustEvent::Left(false));
    }

    if keys.just_pressed(KeyCode::KeyD) || keys.just_pressed(KeyCode::ArrowRight) {
        thrust_event.write(ThrustEvent::Right(true));
    } else if keys.just_released(KeyCode::KeyD) || keys.just_released(KeyCode::ArrowRight) {
        thrust_event.write(ThrustEvent::Right(false));
    }
}

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +5 -2
@@ 1,5 1,6 @@
mod incoming_planets;
mod incoming_parts;
mod key_input;

use std::net::{SocketAddr, UdpSocket};
use std::time::SystemTime;


@@ 13,6 14,7 @@ use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
use bevy_replicon_renet2::RenetChannelsExt;
use crate::client::incoming_parts::incoming_parts_plugin;
use crate::client::incoming_planets::incoming_planets_plugin;
use crate::client::key_input::key_input_plugin;
use crate::ecs::{Ball, CursorWorldCoordinates, Ground, MainCamera, Player, SendBallHere};

pub struct ClientPlugin {


@@ 50,7 52,8 @@ impl Plugin for ClientPlugin {
            .add_systems(Update, follow_camera)
            .add_systems(Update, find_me)
            .add_plugins(incoming_planets_plugin)
            .add_plugins(incoming_parts_plugin);
            .add_plugins(incoming_parts_plugin)
            .add_plugins(key_input_plugin);
    }
}



@@ 94,4 97,4 @@ fn update_cursor_position(
    } else {
        coords.0 = None;
    }
}
\ No newline at end of file
}

M crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +7 -1
@@ 6,6 6,7 @@ use serde::Deserialize;
pub struct GlobalWorldConfig {
    pub world: WorldConfig,
    pub part: PartConfig,
    pub hearty: HeartyConfig,
}

#[derive(Deserialize, Asset, TypePath, Clone)]


@@ 18,4 19,9 @@ pub struct PartConfig {
    pub default_width: f32,
    pub default_height: f32,
    pub default_mass: f32
}
\ No newline at end of file
}

#[derive(Deserialize, Asset, TypePath, Clone)]
pub struct HeartyConfig {
    pub thrust: f32,
}

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +17 -1
@@ 19,6 19,14 @@ pub struct CursorWorldCoordinates(pub Option<Vec2>);
#[derive(Debug, Default, Deserialize, Event, Serialize)]
pub struct SendBallHere(pub Vec2);

#[derive(Debug, Deserialize, Event, Serialize)]
pub enum ThrustEvent {
    Up(bool),
    Down(bool),
    Left(bool),
    Right(bool),
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(ReadMassProperties, RigidBody::Dynamic, ExternalForce, ExternalImpulse)]
pub struct Part {


@@ 38,4 46,12 @@ pub struct PartBundle {
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct Player {
    pub client: Entity
}
\ No newline at end of file
}

#[derive(Component, Default, Serialize, Deserialize, Debug)]
pub struct PlayerThrust {
    pub up: bool,
    pub down: bool,
    pub left: bool,
    pub right: bool,
}

M crates/unified/src/server/gravity.rs => crates/unified/src/server/gravity.rs +2 -6
@@ 5,7 5,7 @@ use crate::config::planet::Planet;
use crate::ecs::Part;
use crate::server::world_config::WorldConfigResource;

pub fn newtonian_gravity_plugin(mut app: &mut App) {
pub fn newtonian_gravity_plugin(app: &mut App) {
    app.add_systems(Update, update_gravity);
}



@@ 25,10 25,6 @@ fn update_gravity(
    let Some(world_config) = &world_config.config else { return; };

    for (part_transform, part_mass, mut forces, mut impulses) in &mut part_query {
        forces.force = Vec2::ZERO;
        impulses.impulse = Vec2::ZERO;
        forces.torque = 0.0;

        let part_mass = part_mass.mass;
        let part_translation = part_transform.translation;



@@ 43,4 39,4 @@ fn update_gravity(
            forces.force += direction.xy();
        }
    }
}
\ No newline at end of file
}

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +1 -1
@@ 55,4 55,4 @@ impl Plugin for ServerPlugin {
            .add_plugins(newtonian_gravity_plugin)
            .add_plugins(player_management_plugin);
    }
}
\ No newline at end of file
}

M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +85 -6
@@ 1,12 1,26 @@
use std::f32::consts::PI;

use bevy::prelude::*;
use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider, MassProperties, ReadMassProperties, RigidBody};
use bevy_replicon::prelude::{ConnectedClient, Replicated};
use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider, ExternalForce, ExternalImpulse, MassProperties, ReadMassProperties, RigidBody, Sensor};
use bevy_replicon::prelude::{ConnectedClient, FromClient, Replicated};
use crate::config::planet::Planet;
use crate::ecs::{Part, PartBundle, Player};
use crate::ecs::{Part, PartBundle, Player, PlayerThrust, ThrustEvent};
use crate::server::world_config::WorldConfigResource;

pub fn player_management_plugin(mut app: &mut App) {
    app.add_systems(Update, handle_new_players);
pub fn player_management_plugin(app: &mut App) {
    app
        .add_systems(PreUpdate, reset_movement)
        .add_systems(Update, (handle_new_players, player_thrust));
}

fn reset_movement(
    mut players: Query<(&mut ExternalForce, &mut ExternalImpulse)>
) {
    for (mut force, mut impulse) in &mut players {
        force.force = Vec2::ZERO;
        force.torque = 0.0;
        impulse.impulse = Vec2::ZERO;
    }
}

fn handle_new_players(mut commands: Commands, q_new_clients: Query<Entity, Added<ConnectedClient>>, world_config: Res<WorldConfigResource>, planets: Query<(&Transform, &Planet)>) {


@@ 38,8 52,73 @@ fn handle_new_players(mut commands: Commands, q_new_clients: Query<Entity, Added
                })
            })
            .insert(Replicated)
            .insert(ExternalForce::default())
            .insert(PlayerThrust::default())
            .insert(Player {
                client: joined_player,
            });
    }
}
\ No newline at end of file
}

fn player_thrust(
    mut players: Query<(&Transform, &mut ExternalForce, &mut PlayerThrust)>,
    mut thrust_event: EventReader<FromClient<ThrustEvent>>,
    world_config: Res<WorldConfigResource>,
) {
    use ThrustEvent::*;
    for event in thrust_event.read() {
        let FromClient { client_entity, event } = event;
        let Ok((_, _, mut thrust)) = players.get_mut(*client_entity) else { continue };
        match *event {
            Up(on) => thrust.up = on,
            Down(on) => thrust.down = on,
            Left(on) => thrust.left = on,
            Right(on) => thrust.right = on,
        }
    }
    for (transform, mut force, thrust) in &mut players {
        let Some(world_config) = &world_config.config else { return; };

        let forward = (transform.rotation * Vec3::Y).xy();
        let mut external_force = ExternalForce::default();
        let mut thrusters = [0.0; 4]; // counterclockwise wrt +y axis
        if thrust.up {
            thrusters[1] = 1.0;
            thrusters[2] = 1.0;
        }
        if thrust.down {
            thrusters[0] = 1.0;
            thrusters[3] = 1.0;
        }
        if thrust.left {
            thrusters[0] = 1.0;
            thrusters[2] = 1.0;
        }
        if thrust.right {
            thrusters[1] = 1.0;
            thrusters[3] = 1.0;
        }
        let half_size = Vec2::new(world_config.part.default_width/2.0, world_config.part.default_height/2.0).length();
        external_force += ExternalForce::at_point(
            -forward*thrusters[0]*world_config.hearty.thrust,
            transform.translation.xy() + half_size*Vec2::new((1.0*PI/4.0).cos(), (1.0*PI/4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ExternalForce::at_point(
            forward*thrusters[1]*world_config.hearty.thrust,
            transform.translation.xy() + half_size*Vec2::new((3.0*PI/4.0).cos(), (3.0*PI/4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ExternalForce::at_point(
            forward*thrusters[2]*world_config.hearty.thrust,
            transform.translation.xy() + half_size*Vec2::new((5.0*PI/4.0).cos(), (5.0*PI/4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ExternalForce::at_point(
            -forward*thrusters[3]*world_config.hearty.thrust,
            transform.translation.xy() + half_size*Vec2::new((7.0*PI/4.0).cos(), (7.0*PI/4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        *force += external_force;
    }
}

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +4 -3
@@ 1,9 1,9 @@
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt};
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt, FromClient, ServerEventAppExt};
use crate::config::planet::Planet;
use crate::ecs::{Ball, Ground, Part, Player, SendBallHere};
use crate::ecs::{Ball, Ground, Part, Player, SendBallHere, ThrustEvent};

pub struct SharedPluginGroup;



@@ 21,6 21,7 @@ impl PluginGroup for SharedPluginGroup {
pub fn register_everything(app: &mut App) {
    app
        .add_client_event::<SendBallHere>(Channel::Ordered)
        .add_client_event::<ThrustEvent>(Channel::Ordered)
        .replicate::<Transform>()
        .replicate::<Ball>()
        .replicate::<Ground>()


@@ 40,4 41,4 @@ fn setup_physics(
) {
    let mut cfg = rapier_config.single_mut().unwrap();
    cfg.gravity = Vec2::ZERO;
}
\ No newline at end of file
}