Something went wrong. Try again.
A Quadrilateral Cowboy clone intended to help me learn Game Dev
Something went wrong. Try again.
7.3 kB · 219 lines
Rust
at rust-bevy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219use bevy::{gltf::GltfNode, prelude::*};use bevy_common_assets::ron::RonAssetPlugin;use crate::game::GameState;use std::{fs, path};
pub fn plugin(app: &mut App) { app.add_plugins(RonAssetPlugin::<Level>::new(&["level.ron"])) .add_systems(OnEnter(GameState::Loading), load_levels) .add_systems(OnEnter(GameState::Main), list_levels) .add_systems(OnEnter(GameState::Main), draw_menu) .add_systems(Update, level_buttons.run_if(in_state(GameState::Main))) .add_systems(OnExit(GameState::Main), unload_menu) .add_systems(OnEnter(LoadingSceneDataState), load_level_scene) .add_systems(Update, load_level.run_if(in_state(LoadingLevelState))) .add_computed_state::<LoadingLevelState>() .add_computed_state::<LevelState>() .add_computed_state::<LoadingSceneDataState>() .init_resource::<LevelSceneData>();}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]struct LoadingSceneDataState;
impl ComputedStates for LoadingSceneDataState { type SourceStates = GameState;
fn compute(sources: Self::SourceStates) -> Option<Self> { match sources { GameState::LoadingSceneData(_) => Some(LoadingSceneDataState), _ => None } }}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]struct LoadingLevelState;
impl ComputedStates for LoadingLevelState { type SourceStates = GameState; fn compute(sources: Self::SourceStates) -> Option<Self> { match sources { GameState::LoadingLevel(_) => Some(LoadingLevelState), _ => None } }}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]struct LevelState;
impl ComputedStates for LevelState { type SourceStates = GameState;
fn compute(sources: Self::SourceStates) -> Option<Self> { match sources { GameState::Level(_) => Some(LevelState), _ => None } }}
#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Clone, Debug, PartialEq, Eq, Hash, Default)]pub struct Level { mesh_path: String, pub name: String}
#[derive(Resource)]pub struct Levels { pub levels: Vec<Handle<Level>>}
#[derive(Component)]struct LevelData(pub Level);
#[derive(Component)]struct MainMenu;
#[derive(Component)]struct LevelComp;
fn load_levels(mut commands: Commands, asset_server: Res<AssetServer>, mut next_state: ResMut<NextState<GameState>>) { commands.insert_resource(Levels{ levels: fs::read_dir(path::Path::new("assets").join("levels")).unwrap().map(|entry| -> Option<Handle<Level>> { let entry = entry.unwrap();
if entry.path().is_dir() { None } else if entry.path().to_str().unwrap().ends_with(".level.ron") { Some(asset_server.load(entry.path().to_str().unwrap().replace("assets/", ""))) } else { None } }).filter(|val| -> bool { matches!(val, Some(_)) }).map(|val| -> Handle<Level> { val.unwrap() }).collect::<Vec<Handle<Level>>>() });
next_state.set(GameState::Main);}
fn list_levels(levels: Res<Levels>, level_data: Res<Assets<Level>>) { println!("Levels: {}", levels.levels.iter().map(|val| { level_data.get(val).unwrap().name.clone() }).collect::<Vec<String>>().join(", "));}
fn draw_menu(mut commands: Commands, levels: Res<Levels>, level_data: Res<Assets<Level>>, asset_server: Res<AssetServer>) { commands.spawn((Camera::default(), Camera2d, MainMenu)); commands.spawn(( Node{ width: percent(100), height: percent(100), align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }, MainMenu )).with_children(move |parent| { for level in &levels.levels { let level_handle = level_data.get(level).unwrap(); let a_s = asset_server.clone();
parent.spawn(( Button, Node{ width: px(150), height: px(65), border: UiRect::all(px(5)), justify_content: JustifyContent::Center, align_items: AlignItems::Center, border_radius: BorderRadius::MAX, ..default() }, BorderColor::all(Color::WHITE), BackgroundColor(Color::BLACK), LevelData(level_handle.clone()) )).with_children(move |parent| { parent.spawn(( Text::new(level_handle.name.clone()), TextFont{ font: a_s.load("fonts/NotoSans.ttf"), font_size: 24.0, ..default() }, TextColor(Color::srgb(0.9, 0.9, 0.9)), TextShadow::default() )); }); } });}
fn level_buttons(interaction_query: Query<(&Interaction, &Button, &LevelData), Changed<Interaction>>, mut next_state: ResMut<NextState<GameState>>) { for (interaction, _button, level_data) in &interaction_query { match *interaction { Interaction::Pressed => { next_state.set(GameState::LoadingSceneData(level_data.0.clone())) }, _ => () } }}
fn unload_menu(mut commands: Commands, menu_query: Query<(Entity, &MainMenu)>) { println!("Unloading main menu stuff");
for (entity, _main_menu) in &menu_query { commands.entity(entity).despawn(); }}
#[derive(Resource, Default)]struct LevelSceneData(pub Handle<Gltf>);
fn load_level_scene(state: Res<State<GameState>>, asset_server: Res<AssetServer>, mut next_state: ResMut<NextState<GameState>>, mut scene_data: ResMut<LevelSceneData>) { let level_data = match state.clone() { GameState::LoadingSceneData(val) => { val.to_owned() }, _ => panic!("This shouldn't fire, but panic if it does") };
println!("Loading {}", &level_data.name); scene_data.0 = asset_server.load(level_data.mesh_path.clone()); next_state.set(GameState::LoadingLevel(level_data));}
fn load_level(mut commands: Commands, state: Res<State<GameState>>, scene_data: Res<LevelSceneData>, gltfs: Res<Assets<Gltf>>, gltf_nodes: Res<Assets<GltfNode>>, mut next_state: ResMut<NextState<GameState>>) { let level_data = match state.clone() { GameState::LoadingLevel(val) => { val.to_owned() }, _ => panic!("This shouldn't fire, but panic if it does") };
if let Some(scene_node) = gltfs.get(&scene_data.0) {
commands.spawn(( LevelComp, SceneRoot(scene_node.scenes[0].clone()) ));
if let Some(player_start_node) = gltf_nodes.get(scene_node.named_nodes.get("PlayerStart").unwrap()) { commands.spawn(( Camera3d::default(), Camera::default(), player_start_node.transform.clone() )); } else { panic!("Could not get player start node"); }
next_state.set(GameState::Level(level_data.clone())); }}