added manual ball controll with keyboard

This commit is contained in:
Elmar Kresse
2025-07-06 20:44:45 +02:00
parent f967a2d8a1
commit c115a1c1ed

View File

@ -20,7 +20,8 @@ struct Vertex {
} }
impl Vertex { impl Vertex {
fn desc() -> wgpu::VertexBufferLayout<'static> { // Returns the vertex buffer layout for the Vertex structure - VertexBufferLayout Specifies an interpretation of the bytes of a vertex buffer as vertex attributes. fn desc() -> wgpu::VertexBufferLayout<'static> {
// Returns the vertex buffer layout for the Vertex structure - VertexBufferLayout Specifies an interpretation of the bytes of a vertex buffer as vertex attributes.
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, // The size of a single vertex in bytes array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, // The size of a single vertex in bytes
step_mode: wgpu::VertexStepMode::Vertex, // Indicates that each vertex has its own attributes if `VertexStepMode::Vertex` is used, or that the attributes are shared across instances if `VertexStepMode::Instance` is used. step_mode: wgpu::VertexStepMode::Vertex, // Indicates that each vertex has its own attributes if `VertexStepMode::Vertex` is used, or that the attributes are shared across instances if `VertexStepMode::Instance` is used.
@ -72,6 +73,8 @@ struct Ball {
velocity: [f32; 2], velocity: [f32; 2],
radius: f32, radius: f32,
color: [f32; 3], // RGB color color: [f32; 3], // RGB color
manual_control: bool, // Add this field
speed: f32, // Add this field for manual control speed
} }
impl Ball { impl Ball {
@ -81,45 +84,71 @@ impl Ball {
velocity: [0.5, 0.5], // Initial velocity <-- x, y velocity: [0.5, 0.5], // Initial velocity <-- x, y
radius: 0.1, // Radius of the ball radius: 0.1, // Radius of the ball
color: [1.0, 0.5, 0.2], // Orange color color: [1.0, 0.5, 0.2], // Orange color
manual_control: false, // Initially not under manual control
speed: 1.0, // Default speed
} }
} }
fn update(&mut self, dt: f32, window_width: f32, window_height: f32) { fn update(&mut self, dt: f32, window_width: f32, window_height: f32) {
// Update position
self.position[0] += self.velocity[0] * dt; // Update x position
self.position[1] += self.velocity[1] * dt; // Update y position
// Convert window dimensions to normalized coordinates // Convert window dimensions to normalized coordinates
let aspect_ratio = window_width / window_height; let aspect_ratio = window_width / window_height;
let bounds_x = aspect_ratio; let bounds_x = aspect_ratio;
let bounds_y = 1.0; let bounds_y = 1.0;
// update ball size according to window size // update ball size according to window size
self.radius = 0.1 * (window_width / 800.0); // // Assuming the original window width is 800px self.radius = 0.1 * (window_width / 800.0); // Assuming the original window width is 800px
if !self.manual_control {
// Update position for automatic movement
self.position[0] += self.velocity[0] * dt;
self.position[1] += self.velocity[1] * dt;
// Bounce off walls // Bounce off walls
if self.position[0] + self.radius > bounds_x || self.position[0] - self.radius < -bounds_x { if self.position[0] + self.radius > bounds_x
self.velocity[0] = -self.velocity[0] + 0.1 * rand::random::<f32>(); // Add a small random factor to the velocity || self.position[0] - self.radius < -bounds_x
self.position[0] = self.position[0].clamp(-bounds_x + self.radius, bounds_x - self.radius); {
self.velocity[0] = -self.velocity[0];
// assign a new random color when bouncing off the wall // assign a new random color when bouncing off the wall
self.color = [ self.color = [
rand::random::<f32>(), 0.2 + 0.8 * (self.position[0].abs() / bounds_x),
rand::random::<f32>(), 0.2 + 0.8 * (self.position[1].abs() / bounds_y),
rand::random::<f32>(), 0.2 + 0.6 * ((self.position[0].abs() + self.position[1].abs()) / (bounds_x + bounds_y)),
]; ];
} }
// if the y position plus the radius is greater than the bounds_y or the y position minus the radius is less than -bounds_y, then bounce off the wall // if the y position plus the radius is greater than the bounds_y or the y position minus the radius is less than -bounds_y, then bounce off the wall
if self.position[1] + self.radius > bounds_y || self.position[1] - self.radius < -bounds_y { if self.position[1] + self.radius > bounds_y
self.velocity[1] = -self.velocity[1] + 0.1 * rand::random::<f32>(); // Add a small random factor to the velocity || self.position[1] - self.radius < -bounds_y
self.position[1] = self.position[1].clamp(-bounds_y + self.radius, bounds_y - self.radius); {
self.velocity[1] = -self.velocity[1];
// assign a new random color when bouncing off the wall // assign a new random color when bouncing off the wall
self.color = [ self.color = [
rand::random::<f32>(), 0.2 + 0.8 * (self.position[0].abs() / bounds_x),
rand::random::<f32>(), 0.2 + 0.8 * (self.position[1].abs() / bounds_y),
rand::random::<f32>(), 0.2 + 0.6 * ((self.position[0].abs() + self.position[1].abs()) / (bounds_x + bounds_y)),
]; ];
} }
} }
// Clamp position to bounds for both manual and automatic control
self.position[0] = self.position[0].clamp(-bounds_x + self.radius, bounds_x - self.radius);
self.position[1] = self.position[1].clamp(-bounds_y + self.radius, bounds_y - self.radius);
}
fn move_ball(&mut self, direction: [f32; 2], dt: f32) {
if self.manual_control {
self.position[0] += direction[0] * self.speed * dt;
self.position[1] += direction[1] * self.speed * dt;
}
}
fn toggle_control_mode(&mut self) {
self.manual_control = !self.manual_control;
if self.manual_control {
self.color = [0.2, 1.0, 0.2]; // Green when manually controlled
} else {
self.color = [1.0, 0.5, 0.2]; // Orange when automatic
}
}
} }
struct State { struct State {
@ -137,6 +166,7 @@ struct State {
uniforms: Uniforms, uniforms: Uniforms,
ball: Ball, ball: Ball,
last_frame_time: Instant, last_frame_time: Instant,
movement_direction: [f32; 2], // Add this field
} }
impl State { impl State {
@ -160,15 +190,13 @@ impl State {
.await?; .await?;
let (device, queue) = adapter let (device, queue) = adapter
.request_device( .request_device(&wgpu::DeviceDescriptor {
&wgpu::DeviceDescriptor {
required_features: wgpu::Features::empty(), required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default(), required_limits: wgpu::Limits::default(),
label: None, label: None,
memory_hints: Default::default(), memory_hints: Default::default(),
trace: wgpu::Trace::default(), trace: wgpu::Trace::default(),
} })
)
.await?; .await?;
let surface_caps = surface.get_capabilities(&adapter); let surface_caps = surface.get_capabilities(&adapter);
@ -338,6 +366,7 @@ impl State {
uniforms, uniforms,
ball, ball,
last_frame_time: Instant::now(), last_frame_time: Instant::now(),
movement_direction: [0.0, 0.0], // Initialize movement direction
}) })
} }
@ -355,11 +384,14 @@ impl State {
let dt = (now - self.last_frame_time).as_secs_f32(); let dt = (now - self.last_frame_time).as_secs_f32();
self.last_frame_time = now; self.last_frame_time = now;
self.ball.move_ball(self.movement_direction, dt);
// Update ball physics // Update ball physics
self.ball.update(dt, self.size.width as f32, self.size.height as f32); self.ball
.update(dt, self.size.width as f32, self.size.height as f32);
// Update uniforms // Update uniforms
self.uniforms.update_position(self.ball.position[0], self.ball.position[1]); self.uniforms
.update_position(self.ball.position[0], self.ball.position[1]);
self.queue.write_buffer( self.queue.write_buffer(
&self.uniform_buffer, &self.uniform_buffer,
0, 0,
@ -369,13 +401,36 @@ impl State {
self.update_vertex_colors(); self.update_vertex_colors();
} }
fn handle_key_input(&mut self, key_code: KeyCode, pressed: bool) {
// Define key mappings: (axis_index, direction_value)
let key_mapping = match key_code {
// Some is a Rust Type Option that is used to indicate a valid mapping
KeyCode::KeyW => Some((1, 1.0)), // Y-axis, positive
KeyCode::KeyS => Some((1, -1.0)), // Y-axis, negative
KeyCode::KeyA => Some((0, -1.0)), // X-axis, negative
KeyCode::KeyD => Some((0, 1.0)), // X-axis, positive
KeyCode::Space if pressed => {
self.ball.toggle_control_mode();
return;
}
_ => None,
};
// Update movement direction based on key input
if let Some((axis, direction)) = key_mapping {
if pressed {
self.movement_direction[axis] = direction;
} else if self.movement_direction[axis].signum() == direction.signum() {
self.movement_direction[axis] = 0.0;
}
}
}
fn update_vertex_colors(&mut self) { fn update_vertex_colors(&mut self) {
let mut vertices = Vec::new(); let mut vertices = Vec::new();
let segments = 32; // Number of segments for the circle let segments = 32; // Number of segments for the circle
let radius = self.ball.radius; let radius = self.ball.radius;
// Center vertex with ball's color // Center vertex with ball's color
vertices.push(Vertex { vertices.push(Vertex {
position: [0.0, 0.0, 0.0], position: [0.0, 0.0, 0.0],
@ -395,7 +450,9 @@ impl State {
} }
// Update the vertex buffer with new colors // Update the vertex buffer with new colors
self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { self.vertex_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX, usage: wgpu::BufferUsages::VERTEX,
@ -483,6 +540,18 @@ impl ApplicationHandler for App {
}, },
.. ..
} => event_loop.exit(), } => event_loop.exit(),
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: key_state,
physical_key: PhysicalKey::Code(key_code),
..
},
..
} => {
let pressed = key_state == ElementState::Pressed;
state.handle_key_input(key_code, pressed);
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(physical_size); state.resize(physical_size);
} }