Compare commits
8 Commits
9b71fff4eb
...
trunk
| Author | SHA1 | Date | |
|---|---|---|---|
| 7837ab49b0 | |||
| 62c84aceaf | |||
| 7ac3882e9e | |||
| 129e9ccc5e | |||
| 8b290fdd0d | |||
| 845765513d | |||
| 857aefda03 | |||
| 7f651481f6 |
@@ -8,10 +8,15 @@ use bevy::{
|
||||
use thiserror::Error;
|
||||
use tungstenite::{WebSocket, http::Response, stream::MaybeTlsStream};
|
||||
|
||||
use crate::ui::{despawn_main_menu, spawn_main_menu};
|
||||
use crate::ui::{
|
||||
despawn_connection_wait_screen, despawn_main_menu, spawn_connection_wait_screen,
|
||||
spawn_main_menu,
|
||||
};
|
||||
|
||||
mod ui;
|
||||
|
||||
const PADDLE_GAP: f32 = 20.0; // gap between the paddles and the wall (window border)
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
@@ -21,8 +26,25 @@ fn main() {
|
||||
.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(
|
||||
OnEnter(GameState::Connecting),
|
||||
(
|
||||
spawn_connection_wait_screen,
|
||||
// Closure to immediately dispatch a setup-connection request.
|
||||
|mut messages: MessageWriter<WebSocketConnectionMessage>| {
|
||||
messages.write(WebSocketConnectionMessage::SetupConnection);
|
||||
},
|
||||
),
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(GameState::Connecting),
|
||||
despawn_connection_wait_screen,
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(setup_connection, handle_tasks).run_if(in_state(GameState::Connecting)),
|
||||
)
|
||||
.add_systems(OnEnter(GameState::Playing), setup_game)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
@@ -42,9 +64,10 @@ fn main() {
|
||||
}
|
||||
|
||||
/// Main game state indicator
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, States)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, States)]
|
||||
enum GameState {
|
||||
MainMenu,
|
||||
Connecting,
|
||||
Playing,
|
||||
ConnectionDemo, // TODO: Remove this state.
|
||||
}
|
||||
@@ -65,19 +88,50 @@ fn spawn_camera(mut commands: Commands) {
|
||||
}
|
||||
|
||||
/// Initialize the scene
|
||||
fn setup(
|
||||
fn setup_game(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
window: Single<&Window>,
|
||||
) {
|
||||
commands.spawn(Camera2d);
|
||||
// ball
|
||||
commands.spawn((
|
||||
Ball,
|
||||
Mesh2d(meshes.add(Circle::new(10.0))),
|
||||
MeshMaterial2d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
|
||||
Transform::default(),
|
||||
));
|
||||
|
||||
let paddle_mesh = meshes.add(Rectangle::new(10.0, 100.0));
|
||||
let paddle_material = materials.add(Color::WHITE);
|
||||
// Player 1
|
||||
commands.spawn((
|
||||
Paddle,
|
||||
Mesh2d(paddle_mesh.clone()),
|
||||
MeshMaterial2d(paddle_material.clone()),
|
||||
Transform::from_xyz(-window.width() / 2.0 + PADDLE_GAP, 0.0, 1.0),
|
||||
));
|
||||
// Player 2
|
||||
commands.spawn((
|
||||
Paddle,
|
||||
Mesh2d(paddle_mesh),
|
||||
MeshMaterial2d(paddle_material),
|
||||
Transform::from_xyz(window.width() / 2.0 - PADDLE_GAP, 0.0, 1.0),
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Ball;
|
||||
|
||||
/// Marker component for player paddles
|
||||
///
|
||||
/// Maybe one for each player?
|
||||
/// Maybe it can hold the WebSocket, too.
|
||||
/// *Maybe* I can have one struct with an Option<Ws> to know which
|
||||
/// player is the local one (the one with a socket).
|
||||
#[derive(Component)]
|
||||
struct Paddle;
|
||||
|
||||
/// ECS Component to hold the WebSocket. I guess there's going to be a magic
|
||||
/// entity that controls the networking.
|
||||
#[derive(Component)]
|
||||
@@ -101,7 +155,7 @@ enum WebSocketConnectionMessage {
|
||||
// TODO: Presumably a TeardownConnection, right?
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
enum ConnSetupError {
|
||||
#[error("IO")]
|
||||
Io(#[from] std::io::Error),
|
||||
@@ -184,12 +238,17 @@ fn setup_connection(
|
||||
///
|
||||
/// The task is self-removing, so we don't need to delete the [`WsSetupTask`]
|
||||
/// component here.
|
||||
fn handle_tasks(mut commands: Commands, mut transform_tasks: Query<&mut WsSetupTask>) {
|
||||
fn handle_tasks(
|
||||
mut commands: Commands,
|
||||
mut transform_tasks: Query<&mut WsSetupTask>,
|
||||
mut states: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
for mut task in &mut transform_tasks {
|
||||
if let Some(result) = block_on(future::poll_once(&mut task.0)) {
|
||||
match result {
|
||||
Ok(mut commands_queue) => {
|
||||
commands.append(&mut commands_queue);
|
||||
states.set(GameState::Playing);
|
||||
}
|
||||
Err(e) => info!("Connection failed. Err: {e:?}"),
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ use bevy::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::GameState;
|
||||
|
||||
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);
|
||||
@@ -29,13 +31,14 @@ pub fn spawn_main_menu(mut commands: Commands) {
|
||||
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.");
|
||||
});
|
||||
start_button.observe(
|
||||
|_trigger: On<Pointer<Click>>, mut game_state: ResMut<NextState<GameState>>| {
|
||||
game_state.set(GameState::Connecting);
|
||||
},
|
||||
);
|
||||
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);
|
||||
},
|
||||
@@ -43,8 +46,39 @@ pub fn spawn_main_menu(mut commands: Commands) {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn despawn_main_menu(mut commands: Commands) {
|
||||
warn!("->> ui.rs: despawn_main_menu() is not implemented.");
|
||||
/// Despawns the main menu (which is assumed to be the top-most node)
|
||||
///
|
||||
/// TODO: Add a marker component, but only in debug builds. A unit test can
|
||||
/// assert this condition so I don't actually have to check it at runtime
|
||||
/// on release builds.
|
||||
pub fn despawn_main_menu(
|
||||
mut commands: Commands,
|
||||
top_node: Single<Entity, (With<Node>, Without<ChildOf>)>,
|
||||
) {
|
||||
commands.entity(top_node.into_inner()).despawn();
|
||||
}
|
||||
|
||||
pub fn spawn_connection_wait_screen(mut commands: Commands) {
|
||||
info!("Spawning connecting notice.");
|
||||
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()
|
||||
},
|
||||
children![Text::new("Connecting...")],
|
||||
));
|
||||
}
|
||||
|
||||
pub fn despawn_connection_wait_screen(
|
||||
mut commands: Commands,
|
||||
text_nodes: Single<Entity, (With<Node>, With<Text>)>,
|
||||
) {
|
||||
info!("Despawning connecting notice.");
|
||||
commands.entity(text_nodes.into_inner()).despawn();
|
||||
}
|
||||
|
||||
/// The basic bundle for generic buttons.
|
||||
@@ -77,7 +111,7 @@ 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) {
|
||||
if let Ok((mut bg, mut _border)) = button_colors.get_mut(trigger.entity) {
|
||||
bg.0 = BTN_BG_SELECTED;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +120,7 @@ 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) {
|
||||
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