Compare commits

..

7 Commits

Author SHA1 Message Date
JP Stringham
92c83c7b30 Merge branch 'main' of ssh://192.168.7.152:3001/jp/sailing 2025-12-07 11:45:15 -05:00
JP Stringham
711250d87a Sailing direction against wind update 2025-12-07 11:44:56 -05:00
JP Stringham
122e09a6a9 macos gitignore thing 2025-12-02 12:58:23 -05:00
JP Stringham
8a3b485ece Wind! 2025-11-28 16:42:22 -05:00
JP Stringham
cf4beb0b9a Playing with sails and rudders 2025-11-28 16:25:37 -05:00
JP Stringham
b564b42ade tiny sail 2025-11-28 16:06:03 -05:00
JP Stringham
612b76a45c tiny sail 2025-11-28 16:05:52 -05:00
4 changed files with 105 additions and 28 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
*.DS_Store
/target /target

BIN
assets/sail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

View File

@@ -1,4 +1,4 @@
use bevy::prelude::*; use bevy::{prelude::*, sprite_render::Wireframe2dPlugin};
const MAX_SAILING_SPEED: f32 = 0.9; const MAX_SAILING_SPEED: f32 = 0.9;
const MAX_TURN_SPEED: f32 = 0.1; const MAX_TURN_SPEED: f32 = 0.1;
@@ -18,10 +18,26 @@ struct InputState {
y: f32, y: f32,
} }
#[derive(Component, Default)]
struct SailboatState {
sail_extension: f32,
rudder_dir: f32,
}
#[derive(Default, Resource)]
struct WindConditions {
direction: f32,
strength: f32,
}
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
app.add_plugins(DefaultPlugins) app.add_plugins((DefaultPlugins, Wireframe2dPlugin::default()))
.insert_resource(WindConditions {
strength: 1.0,
..default()
})
.add_systems(Startup, (hello_world, camera_setup, sprite_setup).chain()) .add_systems(Startup, (hello_world, camera_setup, sprite_setup).chain())
.add_systems(Update, (handle_keys, player_physics).chain()); .add_systems(Update, (handle_keys, player_physics).chain());
@@ -39,16 +55,33 @@ fn camera_setup(mut clear_color: ResMut<ClearColor>, mut commands: Commands) {
} }
fn sprite_setup(assets: Res<AssetServer>, mut commands: Commands) { fn sprite_setup(assets: Res<AssetServer>, mut commands: Commands) {
let sprite_handle = assets.load("sprite-0001.png"); let ship_sprite = assets.load("sprite-0001.png");
let sail_sprite = assets.load("sail.png");
commands.spawn(( commands
Sprite { .spawn((
image: sprite_handle, Sprite {
..default() image: ship_sprite,
}, ..default()
EntityPhysics::default(), },
InputState::default(), EntityPhysics::default(),
)); InputState::default(),
SailboatState::default(),
))
.with_child((
Sprite {
image: sail_sprite,
..default()
},
Transform {
translation: Vec3 {
x: 8.0,
y: 1.0,
z: 0.,
},
..default()
},
));
} }
fn handle_keys(mut q_player: Query<&mut InputState>, keys: Res<ButtonInput<KeyCode>>) { fn handle_keys(mut q_player: Query<&mut InputState>, keys: Res<ButtonInput<KeyCode>>) {
@@ -77,32 +110,74 @@ fn handle_keys(mut q_player: Query<&mut InputState>, keys: Res<ButtonInput<KeyCo
} }
} }
fn player_physics(mut q_player: Query<(&InputState, &mut EntityPhysics, &mut Transform)>) { fn player_physics(
let Ok((input, mut physics, mut transform)) = q_player.single_mut() else { mut q_player: Query<(
&InputState,
&mut SailboatState,
&mut EntityPhysics,
&mut Transform,
)>,
wind_conditions: Res<WindConditions>,
) {
let Ok((input, mut boat_state, mut physics, mut transform)) = q_player.single_mut() else {
return; return;
}; };
if input.x < 0. { if input.y > 0. {
physics.turn_speed -= 0.1; boat_state.sail_extension = (1.0 + boat_state.sail_extension) * 0.5;
} else if input.x > 0. { } else if input.y < 0. {
physics.turn_speed += 0.1; boat_state.sail_extension *= 0.5;
} else {
physics.turn_speed *= 0.99;
} }
physics.turn_speed = physics.turn_speed.clamp(-MAX_TURN_SPEED, MAX_TURN_SPEED); boat_state.sail_extension = boat_state.sail_extension.clamp(0., 1.);
physics.heading += physics.turn_speed * 0.1;
if input.y > 0. { physics.speed *= 0.99;
physics.speed += 0.1;
} else if input.y < 0. { // using just the heading is incorrect, as the sail adjusts based on heading vs wind
physics.speed -= 0.1; let sail_dir_x = physics.heading.sin();
} else { let sail_dir_y = physics.heading.cos();
physics.speed *= 0.999;
let wind_dir_x = wind_conditions.direction.sin();
let wind_dir_y = wind_conditions.direction.cos();
let sail_dir_vec = Vec2 {
x: sail_dir_x,
y: sail_dir_y,
};
let wind_dir_vec = Vec2 {
x: wind_dir_x,
y: wind_dir_y,
};
// technically you could put your sail out and try to go backwards but..
// let's not tackle that (Yet?)
let sail_strength = sail_dir_vec
.dot(wind_dir_vec)
.clamp(-0.8, 1.)
.remap(-0.8, 1., 0., 1.)
* wind_conditions.strength;
if boat_state.sail_extension > 0. {
physics.speed += 0.01 * boat_state.sail_extension * sail_strength;
} }
physics.speed = physics.speed.clamp(-MAX_SAILING_SPEED, MAX_SAILING_SPEED); physics.speed = physics.speed.clamp(-MAX_SAILING_SPEED, MAX_SAILING_SPEED);
if input.x < 0. {
boat_state.rudder_dir = (-1.0 + boat_state.rudder_dir) * 0.5;
} else if input.x > 0. {
boat_state.rudder_dir = (1.0 + boat_state.rudder_dir) * 0.5;
} else {
boat_state.rudder_dir *= 0.5;
}
physics.turn_speed *= 0.1;
physics.turn_speed += boat_state.rudder_dir * (physics.speed.abs() / MAX_SAILING_SPEED);
physics.turn_speed = physics.turn_speed.clamp(-MAX_TURN_SPEED, MAX_TURN_SPEED);
physics.heading += physics.turn_speed * 0.1;
let x_speed = physics.heading.sin() * physics.speed; let x_speed = physics.heading.sin() * physics.speed;
let y_speed = physics.heading.cos() * physics.speed; let y_speed = physics.heading.cos() * physics.speed;