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, } impl Iterator for AppStateIter { type Item = AppState; fn next(&mut self) -> Option { 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::::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::() .init_state::() .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, mut eaten_event: EventReader) { score.0 += eaten_event.read().count() as u32; } fn pause_system(keypress: Res>, mut time: ResMut>) { if keypress.just_pressed(KeyCode::KeyP) { if time.is_paused() { time.unpause() } else { time.pause() } } }