Something went wrong. Try again.
A Quadrilateral Cowboy clone intended to help me learn Game Dev
Something went wrong. Try again.
7.1 kB · 215 lines
Rust
at rust-bevy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215pub fn plugin(app: &mut App) { app .add_systems(OnEnter(GameState::Test), run_test) .add_systems(OnEnter(GameState::TestLoading), load_resources) .add_systems(Update, check_input.run_if( in_state(GameState::Test) ));}
#[derive(Component)]struct Terminal;
#[derive(Component, Default)]struct CurrentInput(String);
enum ConsoleEntry { Command(String), Error(String), Output(String)}
#[derive(Component, Default)]struct ConsoleHistory(Vec<ConsoleEntry>);
#[derive(Component)]struct TerminalText;
fn load_resources(mut commands: Commands, asset_server: Res<AssetServer>, mut next_state: ResMut<NextState<GameState>>) { let assets = GameAssets{ terminal_primitive0: asset_server.load("models/terminal.glb#Mesh0/Primitive0"), terminal_primitive1: asset_server.load("models/terminal.glb#Mesh0/Primitive1"), terminal_chassis_mat: asset_server.load("models/terminal.glb#Material0") }; commands.insert_resource(assets); next_state.set(GameState::Test);}
fn run_test(mut commands: Commands, game_assets: Res<GameAssets>, mut meshes: ResMut<Assets<Mesh>>, mut images: ResMut<Assets<Image>>, mut materials: ResMut<Assets<StandardMaterial>>, asset_server: Res<AssetServer>) { if let Some(mesh) = meshes.get_mut(&game_assets.terminal_primitive1) { // A simple Quad (4 vertices) UV mapping let uvs = vec![ [1.0, 1.0], [0.0, 1.0], // Top Left, Top Right [1.0, 0.0], [0.0, 0.0] // Bottom Left, Bottom Right ]; // This overwrites the existing UV_0 attribute mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); } let size = Extent3d{ width: 512, height: 512, ..default() };
let mut image = Image::new_fill( size, TextureDimension::D2, &[0, 0, 0, 0], TextureFormat::Bgra8UnormSrgb, RenderAssetUsages::default() );
image.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT; let image_handle = images.add(image);
commands.spawn(DirectionalLight::default());
let texture_camera = commands.spawn(( Camera2d, Camera{ order: -1, ..default() }, RenderTarget::Image(image_handle.clone().into()), )).id();
commands.spawn(( Node{ width: percent(100), height: percent(100), align_items: AlignItems::Start, ..default() }, BackgroundColor(bevy::color::palettes::css::BLACK.into()), UiTargetCamera(texture_camera) )).with_children(|parent| { parent.spawn(( Text::new(""), TextFont{ font_size: 14.0, font: asset_server.load("fonts/NotoMono.ttf"), ..default() }, TextColor::WHITE, TerminalText )); });
let material_handle = materials.add(StandardMaterial{ base_color_texture: Some(image_handle), reflectance: 0.02, unlit: false, uv_transform: Affine2::from_scale_angle_translation( Vec2::new(1.0, 1.0), // Scale (e.g., 2.0 zooms in) 0.0, // Rotation in radians Vec2::new(0.0, 0.0) // Offset (X, Y) ), ..default() });
// terminal mesh commands.spawn(( Node::default(), Transform::from_translation(Vec3::ZERO).with_rotation(Quat::from_rotation_y(-std::f32::consts::FRAC_PI_2)), CurrentInput::default(), Terminal, ConsoleHistory::default() )).with_children(|parent| { parent.spawn(( Mesh3d(game_assets.terminal_primitive0.clone()), MeshMaterial3d(game_assets.terminal_chassis_mat.clone()) ));
parent.spawn(( Mesh3d(game_assets.terminal_primitive1.clone()), MeshMaterial3d(material_handle) )); });
// light commands.spawn(( PointLight{ shadows_enabled: true, ..default() }, Transform::from_xyz(4.0, 8.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y) ));
// camera commands.spawn(( Camera3d::default(), Transform::from_xyz(0.5, -1.0, 3.5).looking_at(Vec3::new(0.0, -1.0, 0.0), Vec3::Y) ));
commands.spawn(terminal::TerminalSystem); commands.spawn(terminal::TerminalObject::new(String::from("door1"), terminal::TObjectType::Door));}
fn check_input(mut keyboard_input_reader: MessageReader<KeyboardInput>, mut query: Query<(&mut CurrentInput, &Terminal, &mut ConsoleHistory)>, mut terminal_query: Query<(&mut Text, &TerminalText)>, mut terminal_system: Query<&mut terminal::TerminalSystem>, mut commands: Commands) { let mut current_input = query.single_mut().unwrap(); let mut current_terminal_text = terminal_query.single_mut().unwrap(); let mut terminal = terminal_system.single_mut().unwrap();
for keyboard_input in keyboard_input_reader.read() { if !keyboard_input.state.is_pressed() { continue; }
current_terminal_text.0.0.clear();
match (&keyboard_input.logical_key, &keyboard_input.text) { (Key::Enter, _) => { if current_input.0.0.is_empty() { continue; }
current_input.2.0.push(ConsoleEntry::Command(current_input.0.0.to_owned())); let nodes = terminal.parse(current_input.0.0.clone()); commands.trigger(terminal::TerminalEvent::new(nodes)); current_input.0.0.clear(); }, (Key::Backspace, _) => { current_input.0.0.pop(); }, (_, Some(inserted_text)) => { if inserted_text.chars().all(is_printable_char) { current_input.0.0.push_str(inserted_text); } }, _ => continue, }
for entry in current_input.2.0.iter() { match entry { ConsoleEntry::Command(cmd) => { current_terminal_text.0.0 += cmd; }, ConsoleEntry::Output(output) => { current_terminal_text.0.0 += output; // TODO: Need to figure out how to change color of text line-by-line }, ConsoleEntry::Error(error) => { current_terminal_text.0.0 += error; // TODO: Need to figure out how to change color of text line-by-line } }
current_terminal_text.0.0 += "\n"; }
current_terminal_text.0.0 += ¤t_input.0.0; }}
fn is_printable_char(chr: char) -> bool { let is_in_private_use_area = ('\u{e000}'..='\u{f8ff}').contains(&chr) || ('\u{f0000}'..='\u{ffffd}').contains(&chr) || ('\u{100000}'..='\u{10fffd}').contains(&chr); !is_in_private_use_area && !chr.is_ascii_control()}