generated from hheik/bevy-template
Added piece collision
parent
8d091fec47
commit
db238c3248
|
|
@ -17,16 +17,19 @@ pub fn init(app: &mut App) {
|
||||||
|
|
||||||
app.add_event::<tetris::OnPiecePlaced>()
|
app.add_event::<tetris::OnPiecePlaced>()
|
||||||
.register_type::<tetris::GameArea>()
|
.register_type::<tetris::GameArea>()
|
||||||
|
.register_type::<tetris::GameGravity>()
|
||||||
|
.register_type::<tetris::BlockSet>()
|
||||||
.register_type::<tetris::NextPiece>()
|
.register_type::<tetris::NextPiece>()
|
||||||
.register_type::<tetris::Piece>()
|
.register_type::<tetris::Piece>()
|
||||||
.register_type::<tetris::PieceControls>()
|
.register_type::<tetris::PieceControls>()
|
||||||
.register_type::<tetris::ControllablePiece>()
|
.register_type::<tetris::ControllablePiece>()
|
||||||
|
.register_type::<tetris::Gravity>()
|
||||||
.register_type::<tetris::Block>()
|
.register_type::<tetris::Block>()
|
||||||
.register_type::<prefab::PieceType>()
|
.register_type::<prefab::PieceType>()
|
||||||
.register_type::<grid::Grid>()
|
.register_type::<grid::Grid>()
|
||||||
.register_type::<grid::GridTransform>()
|
.register_type::<grid::GridTransform>()
|
||||||
.add_systems(Startup, systems::setup_game_scene)
|
.add_systems(Startup, systems::setup_game_scene)
|
||||||
.add_systems(PreUpdate, (systems::block_control, systems::apply_gravity))
|
.add_systems(PreUpdate, (systems::repeat_inputs, systems::apply_gravity))
|
||||||
.add_systems(Update, (systems::demo_2d, systems::apply_piece_movement))
|
.add_systems(Update, (systems::demo_2d, systems::apply_piece_movement))
|
||||||
.add_systems(PostUpdate, systems::grid_positioning);
|
.add_systems(PostUpdate, systems::grid_positioning);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
mod demo;
|
mod demo;
|
||||||
mod game_scene;
|
mod game_scene;
|
||||||
mod grid;
|
mod grid;
|
||||||
|
mod input;
|
||||||
|
mod movement;
|
||||||
|
|
||||||
pub use demo::*;
|
pub use demo::*;
|
||||||
pub use game_scene::*;
|
pub use game_scene::*;
|
||||||
pub use grid::*;
|
pub use grid::*;
|
||||||
|
pub use input::*;
|
||||||
|
pub use movement::*;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,7 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::game::{
|
use crate::game::{grid::*, prefab::*, tetris::*};
|
||||||
grid::*,
|
|
||||||
prefab::*,
|
|
||||||
tetris::{piece_input, *},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn setup_game_scene(world: &mut World) {
|
pub fn setup_game_scene(world: &mut World) {
|
||||||
let game_area = GameArea::default();
|
let game_area = GameArea::default();
|
||||||
|
|
@ -35,127 +31,6 @@ pub fn setup_game_scene(world: &mut World) {
|
||||||
world.flush();
|
world.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_control(
|
|
||||||
time: Res<Time>,
|
|
||||||
key_input: Res<ButtonInput<KeyCode>>,
|
|
||||||
mut piece_query: Query<&mut PieceControls, With<ControllablePiece>>,
|
|
||||||
) {
|
|
||||||
const SOFT_DROP_SPEED: f32 = 10.0;
|
|
||||||
for mut piece in piece_query.iter_mut() {
|
|
||||||
piece.reset_inputs();
|
|
||||||
if key_input.pressed(KeyCode::ArrowDown) {
|
|
||||||
piece.cumulated_gravity += SOFT_DROP_SPEED * time.delta_secs();
|
|
||||||
}
|
|
||||||
if key_input.just_pressed(KeyCode::Space) {
|
|
||||||
piece.instant_drop = true;
|
|
||||||
}
|
|
||||||
if key_input.just_pressed(KeyCode::ArrowUp) {
|
|
||||||
piece.rotation = Some(piece_input::Rotation::Clockwise);
|
|
||||||
}
|
|
||||||
if key_input.just_pressed(KeyCode::ShiftLeft) {
|
|
||||||
piece.rotation = Some(piece_input::Rotation::CounterClockwise);
|
|
||||||
}
|
|
||||||
match (
|
|
||||||
key_input.pressed(KeyCode::ArrowLeft),
|
|
||||||
key_input.pressed(KeyCode::ArrowRight),
|
|
||||||
) {
|
|
||||||
(true, false) => piece.movement = Some(piece_input::Move::Left),
|
|
||||||
(false, true) => piece.movement = Some(piece_input::Move::Right),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_gravity(
|
|
||||||
mut piece_query: Query<(&mut PieceControls, &ChildOf), With<ControllablePiece>>,
|
|
||||||
time: Res<Time>,
|
|
||||||
gravity_query: Query<&GameGravity>,
|
|
||||||
) {
|
|
||||||
for (mut piece, parent) in piece_query.iter_mut() {
|
|
||||||
if let Ok(gravity) = gravity_query.get(parent.0) {
|
|
||||||
piece.cumulated_gravity += gravity.current * time.delta_secs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InputRepeatState {
|
|
||||||
pub input_type: piece_input::Move,
|
|
||||||
pub cumulated_time: f32,
|
|
||||||
pub next_interval: f32,
|
|
||||||
pub interval_reduction: f32,
|
|
||||||
pub min_interval: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_piece_movement(
|
|
||||||
mut piece_query: Query<(&mut PieceControls, Entity, &ChildOf, &Children)>,
|
|
||||||
mut transform_query: Query<(&mut GridTransform, Has<Block>)>,
|
|
||||||
mut input_repeat: Local<Option<InputRepeatState>>,
|
|
||||||
// mut on_piece_placed: EventWriter<OnPiecePlaced>,
|
|
||||||
time: Res<Time>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
// TODO: Handle piece collision
|
|
||||||
for (mut piece_controls, entity, parent, children) in piece_query.iter_mut() {
|
|
||||||
let (mut grid_transform, _) = transform_query.get_mut(entity).unwrap();
|
|
||||||
// TODO: replace with actual logic
|
|
||||||
if piece_controls.instant_drop {
|
|
||||||
grid_transform.translation.y = 0;
|
|
||||||
// on_piece_placed.write(OnPiecePlaced { piece: entity });
|
|
||||||
commands.trigger_targets(OnPiecePlaced { piece: entity }, parent.parent());
|
|
||||||
*input_repeat = None;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let can_move_down = grid_transform.translation.y > 0;
|
|
||||||
if can_move_down && piece_controls.cumulated_gravity >= 1.0 {
|
|
||||||
piece_controls.cumulated_gravity -= 1.0;
|
|
||||||
grid_transform.translation.y -= 1;
|
|
||||||
}
|
|
||||||
if !can_move_down && piece_controls.cumulated_gravity >= 3.0 {
|
|
||||||
// Force piece placement
|
|
||||||
// on_piece_placed.write(OnPiecePlaced { piece: entity });
|
|
||||||
commands.trigger_targets(OnPiecePlaced { piece: entity }, parent.parent());
|
|
||||||
*input_repeat = None;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(movement) = piece_controls.movement {
|
|
||||||
let mut move_piece = false;
|
|
||||||
if input_repeat
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|state| state.input_type == movement)
|
|
||||||
{
|
|
||||||
if let Some(input_repeat) = input_repeat.as_mut() {
|
|
||||||
input_repeat.cumulated_time += time.delta_secs();
|
|
||||||
if input_repeat.cumulated_time >= input_repeat.next_interval {
|
|
||||||
input_repeat.cumulated_time = 0.0;
|
|
||||||
input_repeat.next_interval = (input_repeat.next_interval
|
|
||||||
* input_repeat.interval_reduction)
|
|
||||||
.max(input_repeat.min_interval);
|
|
||||||
move_piece = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*input_repeat = Some(InputRepeatState {
|
|
||||||
input_type: movement,
|
|
||||||
cumulated_time: 0.0,
|
|
||||||
next_interval: 0.2,
|
|
||||||
interval_reduction: 0.0,
|
|
||||||
min_interval: 0.03,
|
|
||||||
});
|
|
||||||
move_piece = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if move_piece {
|
|
||||||
grid_transform.translation.x += match movement {
|
|
||||||
piece_input::Move::Left => -1,
|
|
||||||
piece_input::Move::Right => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*input_repeat = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_placed_piece(trigger: Trigger<OnPiecePlaced>, world: &mut World) {
|
pub fn handle_placed_piece(trigger: Trigger<OnPiecePlaced>, world: &mut World) {
|
||||||
let game_area = trigger.target();
|
let game_area = trigger.target();
|
||||||
let event = *trigger.event();
|
let event = *trigger.event();
|
||||||
|
|
@ -178,6 +53,10 @@ pub fn handle_placed_piece(trigger: Trigger<OnPiecePlaced>, world: &mut World) {
|
||||||
world.flush();
|
world.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
world
|
||||||
|
.run_system_cached_with(rebuild_collisions, game_area)
|
||||||
|
.expect("Rebuilding collisions");
|
||||||
|
|
||||||
world
|
world
|
||||||
.run_system_cached_with(check_lose_condition, game_area)
|
.run_system_cached_with(check_lose_condition, game_area)
|
||||||
.expect("Checking lose condition");
|
.expect("Checking lose condition");
|
||||||
|
|
@ -187,13 +66,28 @@ pub fn handle_placed_piece(trigger: Trigger<OnPiecePlaced>, world: &mut World) {
|
||||||
.expect("Creating new piece");
|
.expect("Creating new piece");
|
||||||
world.flush();
|
world.flush();
|
||||||
|
|
||||||
// TODO: Finish up clearing the piece / lines
|
|
||||||
// TODO: Lose condition
|
// TODO: Lose condition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rebuild_collisions(
|
||||||
|
In(game_entity): In<Entity>,
|
||||||
|
game_query: Query<&Children>,
|
||||||
|
block_query: Query<&GridTransform, With<Block>>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
if let Ok(children) = game_query.get(game_entity) {
|
||||||
|
let mut blocks = HashSet::new();
|
||||||
|
for child in children.iter() {
|
||||||
|
if let Ok(transform) = block_query.get(child) {
|
||||||
|
blocks.insert(transform.translation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commands.entity(game_entity).insert(BlockSet::new(blocks));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a vector of affected game area entities
|
/// Returns a vector of affected game area entities
|
||||||
fn place_blocks(
|
fn place_blocks(
|
||||||
// mut piece_placed_events: EventReader<OnPiecePlaced>,
|
|
||||||
In((event, game_area)): In<(OnPiecePlaced, Entity)>,
|
In((event, game_area)): In<(OnPiecePlaced, Entity)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
child_query: Query<&Children>,
|
child_query: Query<&Children>,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::game::tetris::*;
|
||||||
|
|
||||||
|
pub fn repeat_inputs(
|
||||||
|
key_input: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut repeat_move: Local<input::InputRepeat>,
|
||||||
|
mut repeat_rotate: Local<input::InputRepeat>,
|
||||||
|
time: Res<Time>,
|
||||||
|
mut controllable_query: Query<&mut PieceControls, With<ControllablePiece>>,
|
||||||
|
) {
|
||||||
|
for mut controls in controllable_query.iter_mut() {
|
||||||
|
*controls = PieceControls::default();
|
||||||
|
let delta_secs = time.delta_secs();
|
||||||
|
|
||||||
|
let move_dir = match (
|
||||||
|
key_input.pressed(KeyCode::ArrowLeft),
|
||||||
|
key_input.pressed(KeyCode::ArrowRight),
|
||||||
|
) {
|
||||||
|
(true, false) => Some(input::Move::Left),
|
||||||
|
(false, true) => Some(input::Move::Right),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
controls.movement = if repeat_move.should_repeat(move_dir.is_some(), delta_secs) {
|
||||||
|
move_dir
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let rotate_dir = match (
|
||||||
|
key_input.pressed(KeyCode::ArrowUp),
|
||||||
|
key_input.pressed(KeyCode::ShiftLeft),
|
||||||
|
) {
|
||||||
|
(true, false) => Some(input::Rotation::Clockwise),
|
||||||
|
(false, true) => Some(input::Rotation::CounterClockwise),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
controls.rotation = if repeat_rotate.should_repeat(rotate_dir.is_some(), delta_secs) {
|
||||||
|
rotate_dir
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if key_input.pressed(KeyCode::ArrowDown) {
|
||||||
|
controls.fast_drop = true;
|
||||||
|
}
|
||||||
|
if key_input.just_pressed(KeyCode::Space) {
|
||||||
|
controls.instant_drop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
game::{grid::*, tetris::*},
|
||||||
|
util::Vector2I,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn apply_gravity(
|
||||||
|
mut piece_query: Query<(&mut Gravity, &PieceControls, &ChildOf)>,
|
||||||
|
time: Res<Time>,
|
||||||
|
game_gravity_query: Query<&GameGravity>,
|
||||||
|
) {
|
||||||
|
const SOFT_DROP_SPEED: f32 = 10.0;
|
||||||
|
for (mut piece_gravity, controls, parent) in piece_query.iter_mut() {
|
||||||
|
if let Ok(game_gravity) = game_gravity_query.get(parent.0) {
|
||||||
|
piece_gravity.cumulated += game_gravity.current * time.delta_secs();
|
||||||
|
}
|
||||||
|
if controls.fast_drop {
|
||||||
|
piece_gravity.cumulated += SOFT_DROP_SPEED * time.delta_secs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_piece_movement(
|
||||||
|
mut piece_query: Query<(
|
||||||
|
&PieceControls,
|
||||||
|
Option<&mut Gravity>,
|
||||||
|
Entity,
|
||||||
|
&ChildOf,
|
||||||
|
&Children,
|
||||||
|
)>,
|
||||||
|
game_query: Query<(&BlockSet, &GameArea)>,
|
||||||
|
mut transform_query: Query<(&mut GridTransform, Has<Block>)>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
for (controls, maybe_gravity, entity, parent, children) in piece_query.iter_mut() {
|
||||||
|
let game_entity = parent.parent();
|
||||||
|
let (collisions, game_area) = game_query.get(game_entity).unwrap();
|
||||||
|
|
||||||
|
let blocks: Vec<Vector2I> = {
|
||||||
|
let mut blocks = vec![];
|
||||||
|
for child in children.iter() {
|
||||||
|
let block = transform_query
|
||||||
|
.get(child)
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(transform, has_block)| {
|
||||||
|
if *has_block {
|
||||||
|
Some(transform.translation)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
if let Some(pos) = block {
|
||||||
|
blocks.push(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blocks
|
||||||
|
};
|
||||||
|
|
||||||
|
let cast_shape =
|
||||||
|
|pos| collisions.cast_shape(pos, &blocks) || !game_area.is_shape_inside(pos, &blocks);
|
||||||
|
|
||||||
|
let piece_pos = &mut transform_query.get_mut(entity).unwrap().0.translation;
|
||||||
|
|
||||||
|
if controls.instant_drop {
|
||||||
|
let mut drop_pos = *piece_pos;
|
||||||
|
while !cast_shape(drop_pos + Vector2I::DOWN) {
|
||||||
|
drop_pos = drop_pos + Vector2I::DOWN;
|
||||||
|
}
|
||||||
|
*piece_pos = drop_pos;
|
||||||
|
commands.trigger_targets(OnPiecePlaced { piece: entity }, game_entity);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let can_move_down = !cast_shape(*piece_pos + Vector2I::DOWN);
|
||||||
|
|
||||||
|
if let Some(mut gravity) = maybe_gravity {
|
||||||
|
if gravity.cumulated >= 1.0 && can_move_down {
|
||||||
|
gravity.cumulated -= 1.0;
|
||||||
|
*piece_pos = *piece_pos + Vector2I::DOWN;
|
||||||
|
}
|
||||||
|
if gravity.cumulated >= 3.0 && !can_move_down {
|
||||||
|
commands.trigger_targets(OnPiecePlaced { piece: entity }, game_entity);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(movement) = controls.movement {
|
||||||
|
let dir = match movement {
|
||||||
|
input::Move::Left => Vector2I::LEFT,
|
||||||
|
input::Move::Right => Vector2I::RIGHT,
|
||||||
|
};
|
||||||
|
if !cast_shape(*piece_pos + dir) {
|
||||||
|
*piece_pos = *piece_pos + dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,42 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::util::Vector2I;
|
use crate::util::Vector2I;
|
||||||
|
|
||||||
use super::{grid::*, prefab::*};
|
use super::{grid::*, prefab::*};
|
||||||
|
|
||||||
|
pub mod input;
|
||||||
|
|
||||||
pub type YPos = i32;
|
pub type YPos = i32;
|
||||||
pub type XPos = i32;
|
pub type XPos = i32;
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Default, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct BlockSet {
|
||||||
|
blocks: HashSet<Vector2I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockSet {
|
||||||
|
pub fn new(blocks: HashSet<Vector2I>) -> Self {
|
||||||
|
Self { blocks }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast_point(&self, pos: &Vector2I) -> bool {
|
||||||
|
self.blocks.contains(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast_shape(&self, shape_pos: Vector2I, local_blocks: &[Vector2I]) -> bool {
|
||||||
|
local_blocks
|
||||||
|
.iter()
|
||||||
|
.map(|local_pos| shape_pos + *local_pos)
|
||||||
|
.any(|global_pos| self.cast_point(&global_pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, Debug, Reflect)]
|
#[derive(Component, Clone, Debug, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
#[require(Grid, NextPiece)]
|
#[require(Grid, NextPiece, BlockSet)]
|
||||||
pub struct GameArea {
|
pub struct GameArea {
|
||||||
pub bottom_boundary: i32,
|
pub bottom_boundary: i32,
|
||||||
pub kill_height: i32,
|
pub kill_height: i32,
|
||||||
|
|
@ -54,6 +81,13 @@ impl GameArea {
|
||||||
&& point.x <= self.right_boundary
|
&& point.x <= self.right_boundary
|
||||||
&& point.y >= self.bottom_boundary
|
&& point.y >= self.bottom_boundary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_shape_inside(&self, shape_pos: Vector2I, local_blocks: &[Vector2I]) -> bool {
|
||||||
|
local_blocks
|
||||||
|
.iter()
|
||||||
|
.map(|local_pos| shape_pos + *local_pos)
|
||||||
|
.all(|global_pos| self.is_point_inside(global_pos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Reflect)]
|
#[derive(Component, Debug, Reflect)]
|
||||||
|
|
@ -68,22 +102,6 @@ impl Default for GameGravity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod piece_input {
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Reflect)]
|
|
||||||
pub enum Rotation {
|
|
||||||
Clockwise,
|
|
||||||
CounterClockwise,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Reflect)]
|
|
||||||
pub enum Move {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Default, Reflect)]
|
#[derive(Component, Debug, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
#[require(Grid, GridTransform)]
|
#[require(Grid, GridTransform)]
|
||||||
|
|
@ -91,20 +109,19 @@ pub struct Piece;
|
||||||
|
|
||||||
#[derive(Component, Debug, Default, Reflect)]
|
#[derive(Component, Debug, Default, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
#[require(Piece)]
|
pub struct Gravity {
|
||||||
pub struct PieceControls {
|
pub cumulated: f32,
|
||||||
pub cumulated_gravity: f32,
|
|
||||||
pub instant_drop: bool,
|
|
||||||
pub movement: Option<piece_input::Move>,
|
|
||||||
pub rotation: Option<piece_input::Rotation>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PieceControls {
|
/// Dictates the intention of piece's movement
|
||||||
pub fn reset_inputs(&mut self) {
|
#[derive(Component, Debug, Default, Reflect)]
|
||||||
self.instant_drop = false;
|
#[reflect(Component)]
|
||||||
self.movement = None;
|
#[require(Piece, Gravity)]
|
||||||
self.rotation = None;
|
pub struct PieceControls {
|
||||||
}
|
pub instant_drop: bool,
|
||||||
|
pub fast_drop: bool,
|
||||||
|
pub movement: Option<input::Move>,
|
||||||
|
pub rotation: Option<input::Rotation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Reflect)]
|
#[derive(Component, Debug, Reflect)]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect)]
|
||||||
|
pub struct InputRepeat {
|
||||||
|
pub currently_pressed: bool,
|
||||||
|
pub cumulated_time: f32,
|
||||||
|
pub next_interval: f32,
|
||||||
|
pub interval_mult: f32,
|
||||||
|
pub min_interval: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InputRepeat {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
currently_pressed: false,
|
||||||
|
cumulated_time: 0.0,
|
||||||
|
next_interval: 0.2,
|
||||||
|
interval_mult: 0.0,
|
||||||
|
min_interval: 0.03,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputRepeat {
|
||||||
|
/// Returns if the input should be repeated this frame.
|
||||||
|
pub fn should_repeat(&mut self, pressed: bool, delta_seconds: f32) -> bool {
|
||||||
|
if !pressed {
|
||||||
|
*self = Self::default();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !self.currently_pressed {
|
||||||
|
self.currently_pressed = true;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.cumulated_time += delta_seconds;
|
||||||
|
if self.cumulated_time >= self.next_interval {
|
||||||
|
self.cumulated_time = 0.0;
|
||||||
|
self.next_interval =
|
||||||
|
(self.next_interval * self.interval_mult).max(self.min_interval);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Reflect)]
|
||||||
|
pub enum Rotation {
|
||||||
|
Clockwise,
|
||||||
|
CounterClockwise,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Reflect)]
|
||||||
|
pub enum Move {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue