Tighten up tetrino grid movement

main
Elijah Voigt 2 months ago
parent b51884ac69
commit 2cab218e5c

@ -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::<Shape>
set_piece.run_if(
any_component_added::<Shape>
.or(any_component_changed::<Shape>)
.or(any_component_added::<Orientation>)
.or(any_component_changed::<Orientation>)
),
set_relative_piece_positions.run_if(
.or(any_component_changed::<Orientation>),
),
shape_block_movement.run_if(
any_component_added::<RelativePosition>
.or(any_component_changed::<RelativePosition>),
.or(any_component_changed::<RelativePosition>)
.or(any_component_changed::<GridPosition>),
),
sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>),
sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>),
@ -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::<Shape>::default()),
(Text::new("ORIENTATION"), SyncSingleton::<Orientation>::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::<Shape>::default()),
(
Text::new("ORIENTATION"),
SyncSingleton::<Orientation>::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<Shape>, Changed<Shape>, Added<Orientation>, Changed<Orientation>)>>,
query: Query<
(Entity, &Shape, &Orientation),
Or<(
Added<Shape>,
Changed<Shape>,
Added<Orientation>,
Changed<Orientation>,
)>,
>,
mut blocks: Query<&mut RelativePosition, With<BlockOf>>,
mut commands: Commands,
visuals: Res<Visuals>,
) {
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::<ShapeBlocks>()
.with_related_entities::<BlockOf>(|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<RelativePosition>, Changed<RelativePosition>)>,
>,
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::<BlockOf>(|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>>) {
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<ShapeBlocks>,
Without<RelativePosition>,
),
>,
mut blocks: Query<(&mut GridPosition, &RelativePosition), With<BlockOf>>,
) {
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<Time>, Local<f32>) -> bool {
move |t: Res<Time>, mut buf: Local<f32>| -> bool {
*buf += t.delta_secs();
@ -541,32 +569,44 @@ fn clock_cycle(n: f32) -> impl FnMut(Res<Time>, Local<f32>) -> bool {
/// Check if the active piece is about to collide with the ground
fn check_collision(
active: Query<&GridPosition, With<RelativePosition>>,
active: Query<(&GridPosition, Entity), With<RelativePosition>>,
shape: Single<Entity, With<Shape>>,
inactive: Query<&GridPosition, Without<RelativePosition>>,
mut commands: Commands,
) {
// TODO: Easy optimizations
let hit = active.iter().any(|a| {
let hit = active.iter().any(|(a, _e)| {
debug!("Checking: {:?}", a);
// Check if active piece is at floor
// Check if active shape is at floor
let hit_floor = a.y == 0;
// Check if active peice is near other pieces
let hit_piece = inactive.iter().any(|b| {
// Check if active peice is near other blocks
let hit_block = inactive.iter().any(|b| {
debug!("Checking against: {:?}", b);
a.is_colliding_with(b)
});
hit_floor || hit_piece
hit_floor || hit_block
});
if hit {
info!("hit!")
// todo!("Do the thing!");
if false {
info!("hit detected!");
let blocks: Vec<Entity> = active
.iter()
.map(|(_, e)| commands.entity(e).remove::<RelativePosition>().id())
.collect();
debug_assert_eq!(blocks.len(), 4, "Shapes should have 4 blocks");
info!("De-relating blocks {:?} and shape {:?}", blocks, *shape);
commands
.entity(*shape)
.remove_related::<BlockOf>(blocks.as_slice())
.despawn();
}
}
fn add_piece(
mut commands: Commands,
) {
fn add_piece(mut commands: Commands) {
// TODO: Choose a different piece
commands.spawn((Orientation::default(), GridPosition::default(), Shape::T));
}

Loading…
Cancel
Save