From 2cab218e5c0bcb675adb81e3721c824d89e4f19e Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sat, 11 Oct 2025 21:49:19 -0700 Subject: [PATCH] Tighten up tetrino grid movement --- src/bin/tetris/main.rs | 434 ++++++++++++++++++++++------------------- 1 file changed, 237 insertions(+), 197 deletions(-) diff --git a/src/bin/tetris/main.rs b/src/bin/tetris/main.rs index c32a460..4dd03f0 100644 --- a/src/bin/tetris/main.rs +++ b/src/bin/tetris/main.rs @@ -25,15 +25,16 @@ fn main() { falling .run_if(in_state(Falling::On)) .run_if(clock_cycle(1.0)), - set_piece - .run_if(any_component_added:: + set_piece.run_if( + any_component_added:: .or(any_component_changed::) .or(any_component_added::) - .or(any_component_changed::) - ), - set_relative_piece_positions.run_if( + .or(any_component_changed::), + ), + shape_block_movement.run_if( any_component_added:: - .or(any_component_changed::), + .or(any_component_changed::) + .or(any_component_changed::), ), sync_singleton_to_ui::.run_if(any_component_changed::), sync_singleton_to_ui::.run_if(any_component_changed::), @@ -153,7 +154,7 @@ impl GridPosition { } fn add_offset(&self, RelativePosition { x: x1, y: y1 }: &RelativePosition) -> Self { - info!("{} + {}, {} + {}", self.x, *x1 as i32, self.y, *y1 as i32); + debug!("{} + {}, {} + {}", self.x, *x1 as i32, self.y, *y1 as i32); GridPosition { x: self.x.checked_add_signed(*x1 as i32).unwrap(), y: self.y.checked_add_signed(*y1 as i32).unwrap(), @@ -237,9 +238,9 @@ impl Orientation { impl Display for Orientation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Orientation::Up => write!(f, "up"), - Orientation::Down => write!(f, "down"), - Orientation::Left => write!(f, "<-"), + Orientation::Up => write!(f, "up"), + Orientation::Down => write!(f, "down"), + Orientation::Left => write!(f, "<-"), Orientation::Right => write!(f, "->"), } } @@ -272,188 +273,196 @@ fn init_pieces( }); } -fn init_debug_ui( - mut commands: Commands, -) { - commands.spawn((Node { - top: Val::Px(0.0), - left: Val::Px(0.0), - ..default() - }, DebuggingState::On)).with_children(|parent| { - parent.spawn(( - Node::default(), - children![ - (Text::new("SHAPE"), SyncSingleton::::default()), - (Text::new("ORIENTATION"), SyncSingleton::::default()), +fn init_debug_ui(mut commands: Commands) { + commands + .spawn(( + Node { + top: Val::Px(0.0), + left: Val::Px(0.0), + ..default() + }, + DebuggingState::On, + )) + .with_children(|parent| { + parent.spawn(( + Node::default(), + children![ + (Text::new("SHAPE"), SyncSingleton::::default()), + ( + Text::new("ORIENTATION"), + SyncSingleton::::default() + ), + ], + )); + }); +} + +#[rustfmt::skip] +fn block_positions(s: &Shape, o: &Orientation) -> [RelativePosition;4] { + match s { + Shape::O => [ + (0,1).into(),(1,1).into(), + (0,0).into(),(1,0).into() + ], + Shape::T => match o { + Orientation::Up => [ + (0,1).into(), + (-1,0).into(),(0,0).into(),(1,0).into(), + ], + Orientation::Down => [ + (-1,0).into(),(0,0).into(),(1,0).into(), + (0,-1).into(), + ], + Orientation::Right => [ + (0,1).into(), + (0,0).into(), (1,0).into(), + (0,-1).into(), + ], + Orientation::Left => [ + (0,1).into(), + (-1,0).into(),(0,0).into(), + (0,-1).into(), ] - )); - }); + }, + Shape::L => match o { + Orientation::Up => [ + (0,1).into(), + (0,0).into(), + (0,-1).into(),(1,-1).into(), + ], + Orientation::Down => [ + (-1,1).into(),(0,1).into(), + (0,0).into(), + (0,-1).into(), + ], + Orientation::Right => [ + (-1,0).into(),(0,0).into(),(1,0).into(), + (-1,-1).into(), + ], + Orientation::Left => [ + (1,1).into(), + (-1,0).into(),(0,0).into(),(1,0).into(), + ], + }, + Shape::J => match o { + Orientation::Up => [ + (0,1).into(), + (0,0).into(), + (-1,-1).into(),(0,-1).into(), + ], + Orientation::Down => [ + (0,1).into(),(1,1).into(), + (0,0).into(), + (0,-1).into(), + ], + Orientation::Left => [ + (-1,0).into(),(0,0).into(),(1,0).into(), + (1,-1).into(), + ], + Orientation::Right => [ + (-1,1).into(), + (-1,0).into(),(0,0).into(),(1,0).into() + ], + }, + Shape::S => match o { + Orientation::Up => [ + (0,0).into(),(1,0).into(), + (-1,-1).into(),(0,-1).into(), + ], + Orientation::Down => [ + (0,1).into(),(1,1).into(), + (-1,0).into(),(0,0).into(), + ], + Orientation::Right => [ + (-1,1).into(), + (-1,0).into(),(0,0).into(), + (0,-1).into(), + ], + Orientation::Left => [ + (0,1).into(), + (0,0).into(),(1,0).into(), + (1,-1).into(), + ], + }, + Shape::Z => match o { + Orientation::Up => [ + (-1,0).into(),(0,0).into(), + (0,-1).into(),(1,-1).into(), + ], + Orientation::Down => [ + (-1,1).into(),(0,1).into(), + (0,0).into(),(1,0).into(), + ], + Orientation::Left => [ + (1,1).into(), + (0,0).into(),(1,0).into(), + (0,-1).into(), + ], + Orientation::Right => [ + (0,1).into(), + (-1,0).into(),(0,0).into(), + (-1,-1).into(), + ], + }, + // TODO: This does not match tetris! + Shape::I => match o { + Orientation::Up => [ + (0,2).into(), + (0,1).into(), + (0,0).into(), + (0,-1).into(), + ], + Orientation::Down => [ + (-1,2).into(), + (-1,1).into(), + (-1,0).into(), + (-1,-1).into(), + ], + Orientation::Left => [ + (-2,0).into(),(-1,0).into(),(0,0).into(),(1,0).into(), + ], + Orientation::Right => [ + (-2,1).into(),(-1,1).into(),(0,1).into(),(1,1).into(), + ] + } + } } fn set_piece( - query: Query<(Entity, &Shape, &Orientation), Or<(Added, Changed, Added, Changed)>>, + query: Query< + (Entity, &Shape, &Orientation), + Or<( + Added, + Changed, + Added, + Changed, + )>, + >, + mut blocks: Query<&mut RelativePosition, With>, mut commands: Commands, visuals: Res, ) { query.iter().for_each(|(e, s, o)| { debug!("{e:?} {s:?} {o:?}"); - commands - .entity(e) - // TODO: Just move entities, do not de/re-spawn - .despawn_related::() - .with_related_entities::(|parent| { - let mesh = visuals.mesh.clone(); - let mat = visuals.material.clone(); - - #[rustfmt::skip] - let piece_positions: [RelativePosition;4] = match s { - Shape::O => [ - (0,1).into(),(1,1).into(), - (0,0).into(),(1,0).into() - ], - Shape::T => match o { - Orientation::Up => [ - (0,1).into(), - (-1,0).into(),(0,0).into(),(1,0).into(), - ], - Orientation::Down => [ - (-1,0).into(),(0,0).into(),(1,0).into(), - (0,-1).into(), - ], - Orientation::Right => [ - (0,1).into(), - (0,0).into(), (1,0).into(), - (0,-1).into(), - ], - Orientation::Left => [ - (0,1).into(), - (-1,0).into(),(0,0).into(), - (0,-1).into(), - ] - }, - Shape::L => match o { - Orientation::Up => [ - (0,1).into(), - (0,0).into(), - (0,-1).into(),(1,-1).into(), - ], - Orientation::Down => [ - (-1,1).into(),(0,1).into(), - (0,0).into(), - (0,-1).into(), - ], - Orientation::Right => [ - (-1,0).into(),(0,0).into(),(1,0).into(), - (-1,-1).into(), - ], - Orientation::Left => [ - (1,1).into(), - (-1,0).into(),(0,0).into(),(1,0).into(), - ], - }, - Shape::J => match o { - Orientation::Up => [ - (0,1).into(), - (0,0).into(), - (-1,-1).into(),(0,-1).into(), - ], - Orientation::Down => [ - (0,1).into(),(1,1).into(), - (0,0).into(), - (0,-1).into(), - ], - Orientation::Left => [ - (-1,0).into(),(0,0).into(),(1,0).into(), - (1,-1).into(), - ], - Orientation::Right => [ - (-1,1).into(), - (-1,0).into(),(0,0).into(),(1,0).into() - ], - }, - Shape::S => match o { - Orientation::Up => [ - (0,0).into(),(1,0).into(), - (-1,-1).into(),(0,-1).into(), - ], - Orientation::Down => [ - (0,1).into(),(1,1).into(), - (-1,0).into(),(0,0).into(), - ], - Orientation::Right => [ - (-1,1).into(), - (-1,0).into(),(0,0).into(), - (0,-1).into(), - ], - Orientation::Left => [ - (0,1).into(), - (0,0).into(),(1,0).into(), - (1,-1).into(), - ], - }, - Shape::Z => match o { - Orientation::Up => [ - (-1,0).into(),(0,0).into(), - (0,-1).into(),(1,-1).into(), - ], - Orientation::Down => [ - (-1,1).into(),(0,1).into(), - (0,0).into(),(1,0).into(), - ], - Orientation::Left => [ - (1,1).into(), - (0,0).into(),(1,0).into(), - (0,-1).into(), - ], - Orientation::Right => [ - (0,1).into(), - (-1,0).into(),(0,0).into(), - (-1,-1).into(), - ], - }, - // TODO: This does not match tetris! - Shape::I => match o { - Orientation::Up => [ - (0,2).into(), - (0,1).into(), - (0,0).into(), - (0,-1).into(), - ], - Orientation::Down => [ - (-1,2).into(), - (-1,1).into(), - (-1,0).into(), - (-1,-1).into(), - ], - Orientation::Left => [ - (-2,0).into(),(-1,0).into(),(0,0).into(),(1,0).into(), - ], - Orientation::Right => [ - (-2,1).into(),(-1,1).into(),(0,1).into(),(1,1).into(), - ] - } - }; - - piece_positions.into_iter().for_each(|rp| { - parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp)); - }); - }); - }); -} -fn set_relative_piece_positions( - query: Query< - (Entity, &RelativePosition, &BlockOf), - Or<(Added, Changed)>, - >, - gp: Query<&GridPosition>, - mut commands: Commands, -) { - query.iter().for_each(|(e, rp, co)| { - let parent_gp = gp.get(co.parent).unwrap(); - let gp: GridPosition = parent_gp.add_offset(rp); - info!("Inserting {:?} for {:?} to {:?}", gp, rp, e); - commands.entity(e).insert(gp); + let mesh = visuals.mesh.clone(); + let mat = visuals.material.clone(); + let positions = block_positions(s, o); + + if blocks.is_empty() { + commands + .entity(e) + .with_related_entities::(|parent| { + positions.into_iter().for_each(|rp| { + parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp)); + }); + }); + } else { + let mut p = positions.into_iter(); + blocks.iter_mut().for_each(|mut rp| { + *rp = p.next().unwrap(); + }); + } }); } @@ -515,18 +524,37 @@ fn draw_grid(mut gizmos: Gizmos) { .outer_edges(); } -fn falling(mut query: Query<&mut GridPosition>) { - query.iter_mut().for_each(|mut gp| { +fn falling(mut shape: Query<&mut GridPosition, With>) { + shape.iter_mut().for_each(|mut gp| { let next = gp.move_down(); if next != *gp { *gp = next; } else { - // Remove the falling component from this entity + // TODO: What to do here?? } }); } +// Re-implement parenting for movement, but on the grid structure +fn shape_block_movement( + shape: Query< + &GridPosition, + ( + With, + Without, + ), + >, + mut blocks: Query<(&mut GridPosition, &RelativePosition), With>, +) { + shape.iter().for_each(|shape_gp| { + blocks.iter_mut().for_each(|(mut block_gp, block_rp)| { + *block_gp = shape_gp.add_offset(block_rp); + }); + }); +} + // Run condition that returns `true` every `n` seconds +// TODO: Update a resource with the current tick fn clock_cycle(n: f32) -> impl FnMut(Res