Compare commits

..

No commits in common. 'fb535106cae403ec72b94b46f029b52f7cfe6a08' and 'a9b50512e677ba285ef82d586516802f67eeb9a5' have entirely different histories.

@ -1,12 +1,5 @@
// Bevy basically forces "complex types" with Querys
#![allow(clippy::type_complexity)]
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() {
App::new()
.add_plugins(BaseGamePlugin {
@ -15,28 +8,13 @@ fn main() {
game_type: GameType::Two,
..default()
})
.init_state::<Falling>()
.add_systems(Startup, (init_pieces, init_debug_ui))
.add_systems(Startup, init_pieces)
.add_systems(
Update,
(
kb_input.run_if(on_event::<KeyboardInput>),
kb_movement.run_if(on_event::<KeyboardInput>),
update_position,
falling
.run_if(in_state(Falling::On))
.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>),
),
sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>),
sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>),
update_orientation,
),
)
.add_systems(Update, draw_grid)
@ -45,117 +23,21 @@ fn main() {
const SCALE: f32 = 30.0;
/// A shape, e.g., the long piece
#[derive(Component, Debug, Default)]
enum Shape {
#[default]
O,
T,
L,
J,
S,
Z,
I,
}
impl Display for Shape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Shape::O => write!(f, "O"),
Shape::T => write!(f, "T"),
Shape::L => write!(f, "L"),
Shape::J => write!(f, "J"),
Shape::S => write!(f, "S"),
Shape::Z => write!(f, "Z"),
Shape::I => write!(f, "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, Default, Debug, Clone, Copy)]
#[require(Transform, Visibility)]
struct GridPosition {
x: usize,
y: usize,
}
impl GridPosition {
fn move_up(&self) -> Self {
Self {
y: if self.y + 1 < 20 {
self.y.saturating_add(1)
} else {
self.y
},
x: self.x,
}
}
fn move_down(&mut self) -> Self {
Self {
y: self.y.saturating_sub(1),
x: self.x,
}
}
fn move_left(&mut self) -> Self {
Self {
x: self.x.saturating_sub(1),
y: self.y,
}
}
fn move_right(&mut self) -> Self {
Self {
x: if self.x + 1 < 10 {
self.x.saturating_add(1)
} else {
self.x
},
y: self.y,
}
}
}
impl Default for GridPosition {
fn default() -> Self {
GridPosition { x: 5, y: 20 }
}
x: isize,
y: isize,
}
impl From<&GridPosition> for Vec3 {
fn from(GridPosition { x, y }: &GridPosition) -> Vec3 {
// Grid Positions start in the bottom left of the area
// 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 = x_0 + ((*x as f32) * SCALE);
let y_0 = -SCALE * 10.0 + (0.5 * SCALE);
let y = y_0 + ((*y as f32) * SCALE);
Vec3::new(x, y, 0.0)
Vec3::new((*x as f32) * SCALE, (*y as f32) * SCALE, 0.0)
}
}
impl From<(usize, usize)> for GridPosition {
fn from((x, y): (usize, usize)) -> GridPosition {
impl From<(isize, isize)> for GridPosition {
fn from((x, y): (isize, isize)) -> GridPosition {
GridPosition { x, y }
}
}
@ -171,12 +53,6 @@ impl std::ops::Add for GridPosition {
}
}
impl std::ops::AddAssign<&GridPosition> for GridPosition {
fn add_assign(&mut self, rhs: &GridPosition) {
*self = *self + *rhs;
}
}
#[derive(Component, Default, Debug)]
enum Orientation {
#[default]
@ -206,28 +82,16 @@ 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::Right => write!(f, "->"),
}
}
}
#[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default, Component)]
enum Falling {
#[default]
On,
Off,
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(Resource, Debug)]
struct Visuals {
material: Handle<ColorMaterial>,
mesh: Handle<Mesh>,
}
fn init_pieces(
@ -235,244 +99,72 @@ fn init_pieces(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.insert_resource(Visuals {
material: materials.add(ColorMaterial {
commands
.spawn((Orientation::default(), GridPosition::default()))
.with_children(|parent| {
let mat = materials.add(ColorMaterial {
color: WHITE.into(),
..default()
}),
mesh: meshes.add(Rectangle::new(SCALE, SCALE)),
});
commands.spawn((Orientation::default(), GridPosition::default(), Shape::T));
}
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()),
]
Mesh2d(meshes.add(Rectangle::new(SCALE, SCALE))),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(0.0, 0.0, 0.0),
));
});
}
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)| {
debug!("{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(),(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),
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,
parent.spawn((
Mesh2d(meshes.add(Rectangle::new(SCALE, SCALE))),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(SCALE, 0.0, 0.0),
));
parent.spawn((
Mesh2d(meshes.add(Rectangle::new(SCALE, SCALE))),
MeshMaterial2d(mat.clone()),
Transform::from_xyz(0.0, SCALE, 0.0),
));
});
}
fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<GridPosition>>) {
query.iter_mut().for_each(|(gp, mut t)| {
let tmp: Vec3 = gp.into();
debug!("Updating position {:?}", tmp);
t.translation = gp.into();
debug!("Updating position {:?}", t.translation);
});
}
fn kb_input(
mut events: EventReader<KeyboardInput>,
mut query: Query<(&mut GridPosition, &mut Orientation, &mut Shape)>,
curr: Res<State<Falling>>,
mut next: ResMut<NextState<Falling>>,
) {
fn update_orientation(mut query: Query<(&Orientation, &mut Transform), Changed<Orientation>>) {
query.iter_mut().for_each(|(o, mut t)| {
t.rotation = o.into();
debug!("Setting orientation to {:?}", o);
});
}
fn kb_movement(mut events: EventReader<KeyboardInput>, mut query: Query<(&mut GridPosition, &mut Orientation)>) {
events.read().for_each(
|KeyboardInput {
key_code, state, ..
}| {
if let ButtonState::Pressed = state {
// TODO: Restict movement based on size/orientation of piece
// Check if children would be outside play area...
query.iter_mut().for_each(|(mut gp, mut o, mut s)| {
match key_code {
// Up arrow should rotate if in falling mode
// Only move up if in falling::off mode
KeyCode::ArrowUp => *o = o.prev(),
KeyCode::ArrowDown => *gp = gp.move_down(),
KeyCode::ArrowLeft => *gp = gp.move_left(),
KeyCode::ArrowRight => *gp = gp.move_right(),
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,
_ => (),
}
let diff: GridPosition = match key_code {
KeyCode::ArrowUp => (0, 1),
KeyCode::ArrowDown => (0, -1),
KeyCode::ArrowLeft => (-1, 0),
KeyCode::ArrowRight => (1, 0),
_ => (0, 0),
}
.into();
query.iter_mut().for_each(|(mut gp, _)| {
debug!("Moving by {:?}", diff);
*gp = *gp + diff;
});
if let KeyCode::Enter = key_code {
query.iter_mut().for_each(|(_, mut o)| {
*o = o.next();
});
}
}
},
);
}
@ -487,27 +179,3 @@ fn draw_grid(mut gizmos: Gizmos) {
)
.outer_edges();
}
fn falling(mut query: Query<&mut GridPosition, With<Shape>>) {
query.iter_mut().for_each(|mut gp| {
let next = gp.move_down();
if next != *gp {
*gp = next;
} else {
// Remove the falling component from this entity
}
});
}
// Run condition that returns `true` every `n` seconds
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();
if *buf > n {
*buf = 0.0;
true
} else {
false
}
}
}

@ -175,23 +175,6 @@ pub fn sync_resource_to_ui<R: Resource + Default + Display>(
});
}
/// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)]
pub struct SyncSingleton<C: Component + Default + Display>(C);
/// Sync a singleton entity's component to the UI
///
/// Mostly useful for quick n' dirty getting data to the user
pub fn sync_singleton_to_ui<C: Component + Default + Display>(
mut q: Query<(&mut Text, &mut Visibility), With<SyncSingleton<C>>>,
c: Single<&C>,
) {
q.iter_mut().for_each(|(mut t, mut v)| {
t.0 = format!("{}", *c);
*v = Visibility::Inherited;
});
}
/// Updates the scroll position of scrollable nodes in response to mouse input
pub fn scroll(trigger: Trigger<Pointer<Scroll>>, mut scrollers: Query<&mut ScrollPosition>) {
let Pointer {

Loading…
Cancel
Save