Begin work on a main menu & UI systems
This commit is contained in:
@@ -8,10 +8,21 @@ use bevy::{
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tungstenite::{WebSocket, http::Response, stream::MaybeTlsStream};
|
use tungstenite::{WebSocket, http::Response, stream::MaybeTlsStream};
|
||||||
|
|
||||||
|
use crate::ui::{despawn_main_menu, spawn_main_menu};
|
||||||
|
|
||||||
|
mod ui;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_systems(Startup, setup)
|
.insert_state(GameState::MainMenu)
|
||||||
|
.add_systems(Startup, spawn_camera)
|
||||||
|
.add_systems(OnEnter(GameState::MainMenu), spawn_main_menu)
|
||||||
|
.add_systems(OnExit(GameState::MainMenu), despawn_main_menu)
|
||||||
|
.add_observer(ui::button_hover_start)
|
||||||
|
.add_observer(ui::button_hover_stop)
|
||||||
|
// TODO: System to operate buttons & other UI widgets
|
||||||
|
// .add_systems(Update, )
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
@@ -20,7 +31,8 @@ fn main() {
|
|||||||
handle_tasks,
|
handle_tasks,
|
||||||
send_info,
|
send_info,
|
||||||
recv_info,
|
recv_info,
|
||||||
),
|
)
|
||||||
|
.run_if(in_state(GameState::ConnectionDemo)),
|
||||||
)
|
)
|
||||||
.add_message::<WebSocketConnectionMessage>()
|
.add_message::<WebSocketConnectionMessage>()
|
||||||
.insert_resource(ChatTimer {
|
.insert_resource(ChatTimer {
|
||||||
@@ -29,6 +41,29 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Main game state indicator
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, States)]
|
||||||
|
enum GameState {
|
||||||
|
MainMenu,
|
||||||
|
Playing,
|
||||||
|
ConnectionDemo, // TODO: Remove this state.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility to despawn entities with a given component. Useful for scene
|
||||||
|
/// changes.
|
||||||
|
///
|
||||||
|
/// I'm using this primarily with marker components to delete UI elements when
|
||||||
|
/// they are no longer needed. E.g.: Removing the main menu after starting.
|
||||||
|
pub fn despawn<T: Component>(mut commands: Commands, targets: Query<Entity, With<T>>) {
|
||||||
|
targets
|
||||||
|
.iter()
|
||||||
|
.for_each(|entt| commands.entity(entt).despawn());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_camera(mut commands: Commands) {
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the scene
|
/// Initialize the scene
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|||||||
92
client/src/ui.rs
Normal file
92
client/src/ui.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//! All the UI elements for the Pong game client.
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
color::palettes::css::{DARK_GRAY, GRAY},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BTN_BORDER_COLOR: Color = Color::WHITE;
|
||||||
|
pub const BTN_BG_COLOR: Color = Color::BLACK;
|
||||||
|
pub const BTN_BG_SELECTED: Color = bevy::prelude::Color::Srgba(DARK_GRAY);
|
||||||
|
|
||||||
|
pub fn spawn_main_menu(mut commands: Commands) {
|
||||||
|
commands
|
||||||
|
.spawn((Node {
|
||||||
|
width: Val::Percent(100.0),
|
||||||
|
height: Val::Percent(100.0),
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
flex_direction: FlexDirection::Column,
|
||||||
|
..Default::default()
|
||||||
|
},))
|
||||||
|
.with_children(|cmds| {
|
||||||
|
cmds.spawn((
|
||||||
|
// TODO: A more sharp and square font, maybe "pixel art" bitmap
|
||||||
|
// to really get the chunky feel of Pong.
|
||||||
|
Text::new("Robert's Bad Pong Game"),
|
||||||
|
TextFont::from_font_size(50.0),
|
||||||
|
TextLayout::new_with_justify(Justify::Center),
|
||||||
|
TextShadow::default(),
|
||||||
|
));
|
||||||
|
let mut start_button = cmds.spawn(button_bundle("Start game"));
|
||||||
|
start_button.observe(|_trigger: On<Pointer<Click>>| {
|
||||||
|
info!("The start button was pressed.");
|
||||||
|
});
|
||||||
|
let mut quit_button = cmds.spawn(button_bundle("Quit Game"));
|
||||||
|
quit_button.observe(
|
||||||
|
|_trigger: On<Pointer<Click>>, mut messages: MessageWriter<AppExit>| {
|
||||||
|
info!("The quit button was pressed.");
|
||||||
|
// Quit the game if the quit button was pressed.
|
||||||
|
messages.write(AppExit::Success);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn despawn_main_menu(mut commands: Commands) {
|
||||||
|
warn!("->> ui.rs: despawn_main_menu() is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The basic bundle for generic buttons.
|
||||||
|
///
|
||||||
|
/// It's mostly so I don't have to copy & paste this everywhere I want to use it.
|
||||||
|
fn button_bundle(text: &str) -> impl Bundle {
|
||||||
|
(
|
||||||
|
Button,
|
||||||
|
Node {
|
||||||
|
width: Val::Px(150.0),
|
||||||
|
height: Val::Px(65.0),
|
||||||
|
border: UiRect::all(Val::Px(4.0)),
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
margin: UiRect::all(Val::Px(10.0)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
BorderColor::all(BTN_BORDER_COLOR),
|
||||||
|
BorderRadius::ZERO,
|
||||||
|
BackgroundColor(BTN_BG_COLOR),
|
||||||
|
children![(
|
||||||
|
Text::new(text),
|
||||||
|
TextColor(GRAY.into()),
|
||||||
|
TextShadow::default(),
|
||||||
|
)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_hover_start(
|
||||||
|
trigger: On<Pointer<Over>>,
|
||||||
|
mut button_colors: Query<(&mut BackgroundColor, &mut BorderColor), With<Button>>,
|
||||||
|
) {
|
||||||
|
if let Ok((mut bg, mut border)) = button_colors.get_mut(trigger.entity) {
|
||||||
|
bg.0 = BTN_BG_SELECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_hover_stop(
|
||||||
|
trigger: On<Pointer<Out>>,
|
||||||
|
mut button_colors: Query<(&mut BackgroundColor, &mut BorderColor), With<Button>>,
|
||||||
|
) {
|
||||||
|
if let Ok((mut bg, mut border)) = button_colors.get_mut(trigger.entity) {
|
||||||
|
bg.0 = BTN_BG_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user