The basic idea of "Move the tiles instead of rotating the shapes"

It is very buggy, but you get the just if what I'm going for.

I'll fix it up tomorrow.
main
Elijah Voigt 2 weeks ago
parent c62350e382
commit 7ae68f0e4c

@ -1,5 +1,12 @@
// Bevy basically forces "complex types" with Querys
#![allow(clippy::type_complexity)]
use games::*; use games::*;
// TODO: Detect when piece is going to go out of bounds and restirct parent from moving there
// TODO: When shape touches the rest of the pieces, re-parent to line entity
// TODO: When line is "full" (has 10 children) clear line and add to score
fn main() { fn main() {
App::new() App::new()
.add_plugins(BaseGamePlugin { .add_plugins(BaseGamePlugin {
@ -13,11 +20,21 @@ fn main() {
.add_systems( .add_systems(
Update, Update,
( (
kb_movement.run_if(on_event::<KeyboardInput>), kb_input.run_if(on_event::<KeyboardInput>),
update_position, update_position,
update_orientation, falling
toggle_falling.run_if(on_event::<KeyboardInput>), .run_if(in_state(Falling::On))
falling.run_if(in_state(Falling::On)).run_if(clock_cycle(1.0)) .run_if(clock_cycle(1.0)),
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(
any_component_added::<RelativePosition>
.or(any_component_changed::<RelativePosition>),
),
), ),
) )
.add_systems(Update, draw_grid) .add_systems(Update, draw_grid)
@ -26,6 +43,34 @@ fn main() {
const SCALE: f32 = 30.0; const SCALE: f32 = 30.0;
/// A shape, e.g., the long piece
#[derive(Component, Debug)]
enum Shape {
O,
T,
L,
J,
S,
Z,
I,
}
/// A part of a piece, i.e., a single square of a piece
#[derive(Component, Debug)]
struct ShapePiece;
#[derive(Component, Debug)]
struct RelativePosition {
x: i8,
y: i8,
}
impl From<(i8, i8)> for RelativePosition {
fn from((x, y): (i8, i8)) -> RelativePosition {
RelativePosition { x, y }
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq)] #[derive(Component, Debug, Clone, Copy, PartialEq)]
#[require(Transform, Visibility)] #[require(Transform, Visibility)]
struct GridPosition { struct GridPosition {
@ -36,39 +81,44 @@ struct GridPosition {
impl GridPosition { impl GridPosition {
fn move_up(&self) -> Self { fn move_up(&self) -> Self {
Self { Self {
y: if self.y + 1 < 20 { self.y.saturating_add(1) } else { self.y }, y: if self.y + 1 < 20 {
x: self.x self.y.saturating_add(1)
} else {
self.y
},
x: self.x,
} }
} }
fn move_down(&mut self) -> Self { fn move_down(&mut self) -> Self {
Self { Self {
y: self.y.saturating_sub(1), y: self.y.saturating_sub(1),
x: self.x x: self.x,
} }
} }
fn move_left(&mut self) -> Self { fn move_left(&mut self) -> Self {
Self { Self {
x: self.x.saturating_sub(1), x: self.x.saturating_sub(1),
y: self.y y: self.y,
} }
} }
fn move_right(&mut self) -> Self { fn move_right(&mut self) -> Self {
Self { Self {
x: if self.x + 1 < 10 { self.x.saturating_add(1) } else { self.x }, x: if self.x + 1 < 10 {
y: self.y self.x.saturating_add(1)
} else {
self.x
},
y: self.y,
} }
} }
} }
impl Default for GridPosition { impl Default for GridPosition {
fn default() -> Self { fn default() -> Self {
GridPosition { GridPosition { x: 5, y: 20 }
x: 5,
y: 20,
}
} }
} }
@ -77,6 +127,7 @@ impl From<&GridPosition> for Vec3 {
// Grid Positions start in the bottom left of the area // Grid Positions start in the bottom left of the area
// So (0, 0) is the bottom left, (0, 9) is the bottom right, etc // So (0, 0) is the bottom left, (0, 9) is the bottom right, etc
// TODO: Custom offset allowing pieces like O and I to have correct center
let x_0 = -SCALE * 5.0 + (0.5 * SCALE); let x_0 = -SCALE * 5.0 + (0.5 * SCALE);
let x = x_0 + ((*x as f32) * SCALE); let x = x_0 + ((*x as f32) * SCALE);
@ -138,23 +189,17 @@ impl Orientation {
} }
} }
impl From<&Orientation> for Quat {
fn from(other: &Orientation) -> Quat {
let z = match other {
Orientation::Up => 0.0,
Orientation::Left => -PI * 0.5,
Orientation::Down => -PI,
Orientation::Right => -PI * 1.5,
};
Quat::from_rotation_z(z)
}
}
#[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default, Component)] #[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default, Component)]
enum Falling { enum Falling {
#[default] #[default]
On, On,
Off Off,
}
#[derive(Resource, Debug)]
struct Visuals {
material: Handle<ColorMaterial>,
mesh: Handle<Mesh>,
} }
fn init_pieces( fn init_pieces(
@ -162,35 +207,151 @@ fn init_pieces(
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
commands commands.insert_resource(Visuals {
.spawn((Orientation::default(), GridPosition::default())) material: materials.add(ColorMaterial {
.with_children(|parent| { color: WHITE.into(),
let mat = materials.add(ColorMaterial { ..default()
color: WHITE.into(), }),
..default() mesh: meshes.add(Rectangle::new(SCALE, SCALE)),
});
commands.spawn((Orientation::default(), GridPosition::default(), Shape::T));
}
fn set_piece(
query: Query<(Entity, &Shape, &Orientation), Or<(Added<Shape>, Changed<Shape>, Added<Orientation>, Changed<Orientation>)>>,
mut commands: Commands,
visuals: Res<Visuals>,
) {
query.iter().for_each(|(e, s, o)| {
info!("{e:?} {s:?} {o:?}");
commands
.entity(e)
.despawn_related::<Children>()
.with_children(|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(),(0,1).into(),
(-1,-1).into(),
],
Orientation::Left => [
(1,1).into(),
(-1,0).into(),(0,0).into(),(0,1).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 | Orientation::Down => [
(0,0).into(),(1,0).into(),
(-1,-1).into(),(0,-1).into(),
],
Orientation::Left | Orientation::Right => [
(0,1).into(),
(-1,0).into(),(0,0).into(),
(-1,-1).into(),
]
},
Shape::Z => match o {
Orientation::Up | Orientation::Down => [
(-1,0).into(),(0,0).into(),
(0,-1).into(),(1,-1).into(),
],
Orientation::Left | Orientation::Right => [
(0,1).into(),
(0,0).into(),(1,0).into(),
(1,-1).into(),
],
},
Shape::I => match o {
Orientation::Up | Orientation::Down => [
(0,2).into(),
(0,1).into(),
(0,0).into(),
(0,-1).into(),
],
Orientation::Left | Orientation::Right => todo!()
}
};
piece_positions.into_iter().for_each(|rp| {
parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp));
}); });
let mesh = meshes.add(Rectangle::new(SCALE, SCALE));
parent.spawn((
Mesh2d(mesh.clone()),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(0.0, 0.0, 0.0),
));
parent.spawn((
Mesh2d(mesh.clone()),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(SCALE, 0.0, 0.0),
));
parent.spawn((
Mesh2d(mesh.clone()),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(0.0, SCALE, 0.0),
));
parent.spawn((
Mesh2d(mesh.clone()),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(-SCALE, 0.0, 0.0),
));
}); });
});
}
fn set_relative_piece_positions(
query: Query<
(Entity, &RelativePosition),
Or<(Added<RelativePosition>, Changed<RelativePosition>)>,
>,
mut commands: Commands,
) {
query.iter().for_each(|(e, rp)| {
commands.entity(e).insert(Transform::from_xyz(
(rp.x as f32) * SCALE,
(rp.y as f32) * SCALE,
0.0,
));
});
} }
fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<GridPosition>>) { fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<GridPosition>>) {
@ -200,14 +361,12 @@ fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<Gri
}); });
} }
fn update_orientation(mut query: Query<(&Orientation, &mut Transform), Changed<Orientation>>) { fn kb_input(
query.iter_mut().for_each(|(o, mut t)| { mut events: EventReader<KeyboardInput>,
t.rotation = o.into(); mut query: Query<(&mut GridPosition, &mut Orientation, &mut Shape)>,
debug!("Setting orientation to {:?}", o); curr: Res<State<Falling>>,
}); mut next: ResMut<NextState<Falling>>,
} ) {
fn kb_movement(mut events: EventReader<KeyboardInput>, mut query: Query<(&mut GridPosition, &mut Orientation)>) {
events.read().for_each( events.read().for_each(
|KeyboardInput { |KeyboardInput {
key_code, state, .. key_code, state, ..
@ -215,14 +374,26 @@ fn kb_movement(mut events: EventReader<KeyboardInput>, mut query: Query<(&mut Gr
if let ButtonState::Pressed = state { if let ButtonState::Pressed = state {
// TODO: Restict movement based on size/orientation of piece // TODO: Restict movement based on size/orientation of piece
// Check if children would be outside play area... // Check if children would be outside play area...
query.iter_mut().for_each(|(mut gp, mut o)| { query.iter_mut().for_each(|(mut gp, mut o, mut s)| {
match key_code { match key_code {
KeyCode::ArrowUp => *gp = gp.move_up(), // Up arrow should rotate if in falling mode
// Only move up if in falling::off mode
KeyCode::ArrowUp => *o = o.next(),
KeyCode::ArrowDown => *gp = gp.move_down(), KeyCode::ArrowDown => *gp = gp.move_down(),
KeyCode::ArrowLeft => *gp = gp.move_left(), KeyCode::ArrowLeft => *gp = gp.move_left(),
KeyCode::ArrowRight => *gp = gp.move_right(), KeyCode::ArrowRight => *gp = gp.move_right(),
KeyCode::Enter => *o = o.next(), KeyCode::Space => next.set(match curr.get() {
_ => () Falling::On => Falling::Off,
Falling::Off => Falling::On,
}),
KeyCode::Digit1 => *s = Shape::T,
KeyCode::Digit2 => *s = Shape::O,
KeyCode::Digit3 => *s = Shape::L,
KeyCode::Digit4 => *s = Shape::J,
KeyCode::Digit5 => *s = Shape::S,
KeyCode::Digit6 => *s = Shape::Z,
KeyCode::Digit7 => *s = Shape::I,
_ => (),
} }
}); });
} }
@ -241,25 +412,8 @@ fn draw_grid(mut gizmos: Gizmos) {
.outer_edges(); .outer_edges();
} }
fn toggle_falling( fn falling(mut query: Query<&mut GridPosition, With<Shape>>) {
mut events: EventReader<KeyboardInput>, query.iter_mut().for_each(|mut gp| {
curr: Res<State<Falling>>,
mut next: ResMut<NextState<Falling>>,
) {
events.read().for_each(|KeyboardInput { state, key_code, .. }| {
if let ButtonState::Pressed = state && *key_code == KeyCode::Space {
next.set(match curr.get() {
Falling::On => Falling::Off,
Falling::Off => Falling::On,
});
}
})
}
fn falling(
mut query: Query<&mut GridPosition>
) {
query.iter_mut ().for_each(|mut gp| {
let next = gp.move_down(); let next = gp.move_down();
if next != *gp { if next != *gp {
*gp = next; *gp = next;
@ -270,7 +424,7 @@ fn falling(
} }
// Run condition that returns `true` every `n` seconds // Run condition that returns `true` every `n` seconds
fn clock_cycle(n: f32) -> impl FnMut (Res<Time>, Local<f32>) -> bool { fn clock_cycle(n: f32) -> impl FnMut(Res<Time>, Local<f32>) -> bool {
move |t: Res<Time>, mut buf: Local<f32>| -> bool { move |t: Res<Time>, mut buf: Local<f32>| -> bool {
*buf += t.delta_secs(); *buf += t.delta_secs();
if *buf > n { if *buf > n {

Loading…
Cancel
Save