reorganize binaries
This commit is contained in:
49
src/bin/first_app.rs
Normal file
49
src/bin/first_app.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Person;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Name(String);
|
||||
|
||||
fn add_people(mut commands: Commands) {
|
||||
commands.spawn((Person, Name("Elaina Proctor".to_string())));
|
||||
commands.spawn((Person, Name("Renzo Hume".to_string())));
|
||||
commands.spawn((Person, Name("Zayna Nieves".to_string())));
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct GreetTimer(Timer);
|
||||
fn greet_people(time: Res<Time>, mut timer: ResMut<GreetTimer>, query: Query<&Name, With<Person>>) {
|
||||
// update our timer with the time elapsed since the last update
|
||||
// if that caused the timer to finish, we say hello to everyone
|
||||
if timer.0.tick(time.delta()).just_finished() {
|
||||
for name in &query {
|
||||
println!("hello {}!", name.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn change_names(mut query: Query<&mut Name, With<Person>>) {
|
||||
for mut name in &mut query {
|
||||
if name.0 == "Elaina Proctor" {
|
||||
name.0 = "Elain Hume".to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HelloPlugin;
|
||||
|
||||
impl Plugin for HelloPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(GreetTimer(Timer::from_seconds(2.0, TimerMode::Repeating)));
|
||||
app.add_systems(Startup, add_people);
|
||||
app.add_systems(Update, (change_names, greet_people).chain());
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins(HelloPlugin)
|
||||
.run();
|
||||
}
|
||||
322
src/bin/snake.rs
Normal file
322
src/bin/snake.rs
Normal file
@@ -0,0 +1,322 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{prelude::*, time::common_conditions::on_timer, window::PrimaryWindow};
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
use rand::prelude::random;
|
||||
|
||||
const SNAKE_HEAD_COLOR: Color = Color::srgb(0.5, 0.7, 0.5);
|
||||
const SNAKE_SEGMENT_COLOR: Color = Color::srgb(0.3, 0.5, 0.3);
|
||||
const FOOD_COLOR: Color = Color::srgb(0.7, 0.5, 0.7);
|
||||
const ARENA_WIDTH: u32 = 15;
|
||||
const ARENA_HEIGHT: u32 = 10;
|
||||
const WINDOW_WIDTH: f32 = 1280.0;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn opposite(self) -> Self {
|
||||
match self {
|
||||
Self::Left => Self::Right,
|
||||
Self::Right => Self::Left,
|
||||
Self::Up => Self::Down,
|
||||
Self::Down => Self::Up,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct SnakeHead {
|
||||
direction: Direction,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct SnakeSegment;
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
struct SnakeSegments(Vec<Entity>);
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
struct LastTailPosition(Option<Position>);
|
||||
|
||||
#[derive(Component)]
|
||||
struct Food;
|
||||
|
||||
#[derive(Component, Clone, Copy, Eq, PartialEq, Debug)]
|
||||
struct Position {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Size {
|
||||
width: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub fn square(x: f32) -> Self {
|
||||
Self {
|
||||
width: x,
|
||||
height: x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_movement_input(input: Res<ButtonInput<KeyCode>>, mut heads: Query<&mut SnakeHead>) {
|
||||
if let Some(mut head) = heads.iter_mut().next() {
|
||||
let dir: Direction = if input.pressed(KeyCode::ArrowUp) {
|
||||
Direction::Up
|
||||
} else if input.pressed(KeyCode::ArrowDown) {
|
||||
Direction::Down
|
||||
} else if input.pressed(KeyCode::ArrowLeft) {
|
||||
Direction::Left
|
||||
} else if input.pressed(KeyCode::ArrowRight) {
|
||||
Direction::Right
|
||||
} else {
|
||||
head.direction
|
||||
};
|
||||
// allow all inputs except turning around
|
||||
if dir != head.direction.opposite() {
|
||||
head.direction = dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_movement(
|
||||
segments: Res<SnakeSegments>,
|
||||
heads: Query<(Entity, &SnakeHead)>,
|
||||
mut positions: Query<&mut Position>,
|
||||
mut last_tail_position: ResMut<LastTailPosition>,
|
||||
mut game_over_writer: EventWriter<GameOverEvent>,
|
||||
) {
|
||||
// Filter positions to just the segments
|
||||
let segment_positions = segments
|
||||
.0
|
||||
.iter()
|
||||
.map(|entity| *positions.get_mut(*entity).unwrap())
|
||||
.collect::<Vec<Position>>();
|
||||
let (head_entity, head) = heads.single();
|
||||
let mut head_pos = positions.get_mut(head_entity).unwrap();
|
||||
|
||||
match &head.direction {
|
||||
Direction::Up => head_pos.y += 1,
|
||||
Direction::Down => head_pos.y -= 1,
|
||||
Direction::Left => head_pos.x -= 1,
|
||||
Direction::Right => head_pos.x += 1,
|
||||
}
|
||||
|
||||
// Before we move the tail, check if the head has collided with a wall.
|
||||
if head_pos.x < 0
|
||||
|| head_pos.y < 0
|
||||
|| head_pos.x as u32 >= ARENA_WIDTH
|
||||
|| head_pos.y as u32 >= ARENA_HEIGHT
|
||||
{
|
||||
game_over_writer.send(GameOverEvent);
|
||||
}
|
||||
// Before we move the tail, check if the head has collided with the tail.
|
||||
if segment_positions.contains(&head_pos) {
|
||||
println!("triggering GAME OVER tail hit {:?}", &head_pos);
|
||||
game_over_writer.send(GameOverEvent);
|
||||
}
|
||||
|
||||
segment_positions
|
||||
.iter()
|
||||
.zip(segments.0.iter().skip(1))
|
||||
.for_each(|(pos, segment)| {
|
||||
*positions.get_mut(*segment).unwrap() = *pos;
|
||||
});
|
||||
last_tail_position.0 = Some(*segment_positions.last().unwrap());
|
||||
}
|
||||
|
||||
fn game_over(
|
||||
mut commands: Commands,
|
||||
mut game_over_reader: EventReader<GameOverEvent>,
|
||||
segments_resource: ResMut<SnakeSegments>,
|
||||
segments: Query<Entity, With<SnakeSegment>>,
|
||||
head: Query<Entity, With<SnakeHead>>,
|
||||
food: Query<Entity, With<Food>>,
|
||||
) {
|
||||
if game_over_reader.read().next().is_some() {
|
||||
println!("GAME OVER");
|
||||
for e in food.iter().chain(segments.iter()).chain(head.iter()) {
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
spawn_snake(commands, segments_resource);
|
||||
}
|
||||
}
|
||||
|
||||
fn scale_translation(
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut q: Query<(&Size, &mut Transform)>,
|
||||
) {
|
||||
let window = windows.single_mut();
|
||||
for (sprite_size, mut transform) in q.iter_mut() {
|
||||
transform.scale = Vec3::new(
|
||||
sprite_size.width / ARENA_WIDTH as f32 * window.width(),
|
||||
sprite_size.height / ARENA_HEIGHT as f32 * window.height(),
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn position_translation(
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut q: Query<(&Position, &mut Transform)>,
|
||||
) {
|
||||
// We subtract half the window width because our coordinate system starts at the bottom left, and
|
||||
// Translation starts from the center. We then add half the size of a single tile, because we want
|
||||
// our sprites bottom left corner to be at the bottom left of a tile, not the center.
|
||||
fn convert(pos: f32, bound_window: f32, bound_game: f32) -> f32 {
|
||||
let tile_size = bound_window / bound_game;
|
||||
pos / bound_game * bound_window - (bound_window / 2.) + (tile_size / 2.)
|
||||
}
|
||||
|
||||
let window = windows.single_mut();
|
||||
for (pos, mut transform) in q.iter_mut() {
|
||||
transform.translation = Vec3::new(
|
||||
convert(pos.x as f32, window.width(), ARENA_WIDTH as f32),
|
||||
convert(pos.y as f32, window.height(), ARENA_HEIGHT as f32),
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_segment(commands: &mut Commands, pos: Position) -> Entity {
|
||||
commands
|
||||
.spawn(Sprite {
|
||||
color: SNAKE_SEGMENT_COLOR,
|
||||
..Default::default()
|
||||
})
|
||||
.insert(SnakeSegment)
|
||||
.insert(Name::new(String::from("SnakeSegment")))
|
||||
.insert(pos)
|
||||
.insert(Size::square(0.77))
|
||||
.id()
|
||||
}
|
||||
|
||||
fn spawn_food(mut commands: Commands) {
|
||||
commands
|
||||
.spawn(Sprite {
|
||||
color: FOOD_COLOR,
|
||||
..default()
|
||||
})
|
||||
.insert(Position {
|
||||
x: (random::<f32>() * ARENA_WIDTH as f32) as i32,
|
||||
y: (random::<f32>() * ARENA_HEIGHT as f32) as i32,
|
||||
})
|
||||
.insert(Name::new(String::from("Food")))
|
||||
.insert(Size::square(0.8))
|
||||
.insert(Food);
|
||||
}
|
||||
|
||||
fn spawn_snake(mut commands: Commands, mut segments: ResMut<SnakeSegments>) {
|
||||
*segments = SnakeSegments(vec![
|
||||
commands
|
||||
.spawn(Sprite {
|
||||
color: SNAKE_HEAD_COLOR,
|
||||
..default()
|
||||
})
|
||||
.insert(SnakeHead {
|
||||
direction: Direction::Right,
|
||||
})
|
||||
.insert(Size::square(1.0))
|
||||
.insert(Name::new(String::from("Snake Head")))
|
||||
.insert(Position { x: 1, y: 0 })
|
||||
.id(),
|
||||
spawn_segment(&mut commands, Position { x: 0, y: 0 }),
|
||||
])
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
struct GrowthEvent;
|
||||
|
||||
#[derive(Event)]
|
||||
struct GameOverEvent;
|
||||
|
||||
fn snake_eating(
|
||||
mut commands: Commands,
|
||||
mut growth_writer: EventWriter<GrowthEvent>,
|
||||
food_positions: Query<(Entity, &Position), With<Food>>,
|
||||
head_positions: Query<&Position, With<SnakeHead>>,
|
||||
) {
|
||||
let head_pos = head_positions.single();
|
||||
for (ent, food_pos) in food_positions.iter() {
|
||||
if food_pos == head_pos {
|
||||
commands.entity(ent).despawn();
|
||||
growth_writer.send(GrowthEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_growth(
|
||||
mut commands: Commands,
|
||||
last_tail_position: Res<LastTailPosition>,
|
||||
mut segments: ResMut<SnakeSegments>,
|
||||
mut growth_reader: EventReader<GrowthEvent>,
|
||||
) {
|
||||
if growth_reader.read().next().is_some() {
|
||||
segments
|
||||
.0
|
||||
.push(spawn_segment(&mut commands, last_tail_position.0.unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_camera(mut commands: Commands) {
|
||||
commands.spawn((Camera2d {},));
|
||||
}
|
||||
|
||||
pub struct SnakePlugin;
|
||||
impl Plugin for SnakePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, (setup_camera, spawn_snake))
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
snake_movement_input,
|
||||
(spawn_food).run_if(on_timer(Duration::from_secs(3))),
|
||||
),
|
||||
)
|
||||
.add_systems(
|
||||
FixedUpdate,
|
||||
(snake_movement, snake_eating, snake_growth, game_over)
|
||||
.chain()
|
||||
.run_if(on_timer(Duration::from_millis(500))),
|
||||
)
|
||||
.add_systems(PostUpdate, (scale_translation, position_translation));
|
||||
|
||||
app.insert_resource(SnakeSegments::default())
|
||||
.insert_resource(LastTailPosition::default());
|
||||
|
||||
// Events must be added to the app before they can be used
|
||||
// using the 'add_event' method
|
||||
app.add_event::<GrowthEvent>();
|
||||
app.add_event::<GameOverEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(
|
||||
DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "Snake".to_string(),
|
||||
resolution: (
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_WIDTH * (ARENA_HEIGHT as f32 / ARENA_WIDTH as f32),
|
||||
)
|
||||
.into(),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}),
|
||||
)
|
||||
.add_plugins(WorldInspectorPlugin::new())
|
||||
.add_plugins(SnakePlugin)
|
||||
.run();
|
||||
}
|
||||
Reference in New Issue
Block a user