Files
bevy-snake/src/main.rs

144 lines
3.8 KiB
Rust

use crate::{
canvas::CanvasPlugin,
fruit::FruitPlugin,
grid::{SEGMENT_SIZE, SIZE},
level::{LevelPlugin, LevelState},
snake::SnakePlugin,
ui::UiPlugin,
};
use bevy::{prelude::*, render::camera::ScalingMode, window::WindowResolution};
use bevy_asset_loader::prelude::*;
use bevy_tweening::TweeningPlugin;
use std::time::Duration;
#[cfg(debug_assertions)]
use bevy_editor_pls::prelude::*;
mod audio;
mod canvas;
mod fruit;
mod grid;
mod level;
mod snake;
mod ui;
const ASPECT_RATIO: f32 = 16. / 9.;
const WINDOW_WIDTH: f32 = WINDOW_HEIGHT * ASPECT_RATIO;
const WINDOW_HEIGHT: f32 = 720.;
const TICK_PERIOD: Duration = Duration::from_millis(125);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, States)]
enum AppState {
MainMenu,
InGame(LevelState),
}
impl Default for AppState {
fn default() -> Self {
if cfg!(debug_assertions) {
Self::InGame(LevelState::Begin)
} else {
Self::MainMenu
}
}
}
#[derive(Default)]
struct AppStateIter {
current: Option<AppState>,
}
impl Iterator for AppStateIter {
type Item = AppState;
fn next(&mut self) -> Option<Self::Item> {
let next_state = match self.current {
None => Some(AppState::MainMenu),
Some(AppState::MainMenu) => Some(AppState::InGame(LevelState::Begin)),
Some(AppState::InGame(level_state)) => match level_state {
LevelState::Begin => Some(AppState::InGame(LevelState::InGame)),
LevelState::InGame => Some(AppState::InGame(LevelState::End)),
LevelState::End => None,
},
};
if let Some(next_state) = next_state {
self.current = Some(next_state);
}
next_state
}
}
#[derive(Resource)]
struct Score(u32);
fn main() {
let mut app = App::new();
app.insert_resource(ClearColor(Color::BLACK))
.insert_resource(Time::<Fixed>::from_duration(TICK_PERIOD))
.insert_resource(Score(0))
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bevy-Snake".into(),
resizable: true,
resolution: WindowResolution::new(WINDOW_WIDTH, WINDOW_HEIGHT),
..Default::default()
}),
..Default::default()
}))
.init_collection::<audio::Assets>()
.init_state::<AppState>()
.add_plugins((
TweeningPlugin,
SnakePlugin,
FruitPlugin,
CanvasPlugin,
UiPlugin,
LevelPlugin,
))
.add_systems(Startup, setup_camera_system)
.add_systems(Update, update_score_system);
#[cfg(debug_assertions)]
{
app.add_plugins(EditorPlugin::new())
.add_systems(Update, (bevy::window::close_on_esc, pause_system));
}
app.run();
}
fn setup_camera_system(mut commands: Commands) {
let grid_dimensions = f32::from(SIZE) * SEGMENT_SIZE * 1.2;
commands
.spawn(Camera2dBundle {
projection: OrthographicProjection {
scaling_mode: ScalingMode::AutoMin {
min_width: grid_dimensions,
min_height: grid_dimensions,
},
near: -1000.,
..Default::default()
},
..Default::default()
})
.insert(Name::new("Orthographic Camera"));
}
fn update_score_system(mut score: ResMut<Score>, mut eaten_event: EventReader<fruit::EatenEvent>) {
score.0 += eaten_event.read().count() as u32;
}
fn pause_system(keypress: Res<ButtonInput<KeyCode>>, mut time: ResMut<Time<Virtual>>) {
if keypress.just_pressed(KeyCode::KeyP) {
if time.is_paused() {
time.unpause()
} else {
time.pause()
}
}
}