added manual ball controll with keyboard
This commit is contained in:
187
src/main.rs
187
src/main.rs
@ -16,11 +16,12 @@ use winit::{
|
||||
// Represents a vertex with position and color
|
||||
struct Vertex {
|
||||
position: [f32; 3], // 3D position of the vertex <-- x, y, z
|
||||
color: [f32; 3], // Color of the vertex in RGB format <-- r, g, b
|
||||
color: [f32; 3], // Color of the vertex in RGB format <-- r, g, b
|
||||
}
|
||||
|
||||
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 {
|
||||
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.
|
||||
@ -71,53 +72,81 @@ struct Ball {
|
||||
position: [f32; 2],
|
||||
velocity: [f32; 2],
|
||||
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 {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
position: [0.0, 0.0], // Initial position at the center
|
||||
velocity: [0.5, 0.5], // Initial velocity <-- x, y
|
||||
radius: 0.1, // Radius of the ball
|
||||
position: [0.0, 0.0], // Initial position at the center
|
||||
velocity: [0.5, 0.5], // Initial velocity <-- x, y
|
||||
radius: 0.1, // Radius of the ball
|
||||
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) {
|
||||
// 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
|
||||
let aspect_ratio = window_width / window_height;
|
||||
let bounds_x = aspect_ratio;
|
||||
let bounds_y = 1.0;
|
||||
|
||||
// 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
|
||||
|
||||
// Bounce off walls
|
||||
if self.position[0] + self.radius > bounds_x || 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.position[0].clamp(-bounds_x + self.radius, bounds_x - self.radius);
|
||||
// assign a new random color when bouncing off the wall
|
||||
self.color = [
|
||||
rand::random::<f32>(),
|
||||
rand::random::<f32>(),
|
||||
rand::random::<f32>(),
|
||||
];
|
||||
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
|
||||
if self.position[0] + self.radius > bounds_x
|
||||
|| self.position[0] - self.radius < -bounds_x
|
||||
{
|
||||
self.velocity[0] = -self.velocity[0];
|
||||
// assign a new random color when bouncing off the wall
|
||||
self.color = [
|
||||
0.2 + 0.8 * (self.position[0].abs() / bounds_x),
|
||||
0.2 + 0.8 * (self.position[1].abs() / bounds_y),
|
||||
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 self.position[1] + self.radius > bounds_y
|
||||
|| self.position[1] - self.radius < -bounds_y
|
||||
{
|
||||
self.velocity[1] = -self.velocity[1];
|
||||
// assign a new random color when bouncing off the wall
|
||||
self.color = [
|
||||
0.2 + 0.8 * (self.position[0].abs() / bounds_x),
|
||||
0.2 + 0.8 * (self.position[1].abs() / bounds_y),
|
||||
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 self.position[1] + self.radius > bounds_y || 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.position[1].clamp(-bounds_y + self.radius, bounds_y - self.radius);
|
||||
// assign a new random color when bouncing off the wall
|
||||
self.color = [
|
||||
rand::random::<f32>(),
|
||||
rand::random::<f32>(),
|
||||
rand::random::<f32>(),
|
||||
];
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,6 +166,7 @@ struct State {
|
||||
uniforms: Uniforms,
|
||||
ball: Ball,
|
||||
last_frame_time: Instant,
|
||||
movement_direction: [f32; 2], // Add this field
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -160,15 +190,13 @@ impl State {
|
||||
.await?;
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
label: None,
|
||||
memory_hints: Default::default(),
|
||||
trace: wgpu::Trace::default(),
|
||||
}
|
||||
)
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
label: None,
|
||||
memory_hints: Default::default(),
|
||||
trace: wgpu::Trace::default(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
@ -195,28 +223,28 @@ impl State {
|
||||
// Create circle vertices (for the ball)
|
||||
let mut vertices = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
|
||||
|
||||
let segments = 32;
|
||||
let radius = 0.1;
|
||||
|
||||
|
||||
// Center vertex
|
||||
vertices.push(Vertex {
|
||||
position: [0.0, 0.0, 0.0],
|
||||
color: [1.0, 0.2, 0.2], // Orange color
|
||||
});
|
||||
|
||||
|
||||
// Circle vertices
|
||||
for i in 0..segments {
|
||||
let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
|
||||
let x = radius * angle.cos();
|
||||
let y = radius * angle.sin();
|
||||
|
||||
|
||||
vertices.push(Vertex {
|
||||
position: [x, y, 0.0],
|
||||
color: [0.1, 0.5, 0.2], // Orange color
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Create triangles
|
||||
for i in 0..segments {
|
||||
indices.push(0); // Center
|
||||
@ -338,6 +366,7 @@ impl State {
|
||||
uniforms,
|
||||
ball,
|
||||
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();
|
||||
self.last_frame_time = now;
|
||||
|
||||
self.ball.move_ball(self.movement_direction, dt);
|
||||
// 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
|
||||
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.uniform_buffer,
|
||||
0,
|
||||
@ -369,25 +401,48 @@ impl State {
|
||||
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) {
|
||||
let mut vertices = Vec::new();
|
||||
let segments = 32; // Number of segments for the circle
|
||||
let radius = self.ball.radius;
|
||||
|
||||
|
||||
|
||||
// Center vertex with ball's color
|
||||
vertices.push(Vertex {
|
||||
position: [0.0, 0.0, 0.0],
|
||||
color: self.ball.color,
|
||||
});
|
||||
|
||||
|
||||
// Circle vertices with ball's color
|
||||
for i in 0..segments {
|
||||
let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
|
||||
let x = radius * angle.cos();
|
||||
let y = radius * angle.sin();
|
||||
|
||||
|
||||
vertices.push(Vertex {
|
||||
position: [x, y, 0.0],
|
||||
color: self.ball.color,
|
||||
@ -395,11 +450,13 @@ impl State {
|
||||
}
|
||||
|
||||
// Update the vertex buffer with new colors
|
||||
self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(&vertices),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
self.vertex_buffer = self
|
||||
.device
|
||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(&vertices),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
}
|
||||
|
||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||
@ -483,6 +540,18 @@ impl ApplicationHandler for App {
|
||||
},
|
||||
..
|
||||
} => 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) => {
|
||||
state.resize(physical_size);
|
||||
}
|
||||
@ -494,7 +563,7 @@ impl ApplicationHandler for App {
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(),
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
}
|
||||
|
||||
|
||||
// Request another frame for continuous animation
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
@ -518,7 +587,7 @@ fn main() -> Result<()> {
|
||||
let event_loop = EventLoop::new()?;
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
let mut app = App {
|
||||
let mut app = App {
|
||||
state: None,
|
||||
window: None,
|
||||
};
|
||||
@ -526,4 +595,4 @@ fn main() -> Result<()> {
|
||||
event_loop.run_app(&mut app)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user