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 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};
|
use crate::ui::{
|
||||||
|
despawn_connection_wait_screen, despawn_main_menu, spawn_connection_wait_screen,
|
||||||
|
spawn_main_menu,
|
||||||
|
};
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
|
const PADDLE_GAP: f32 = 20.0; // gap between the paddles and the wall (window border)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
@@ -21,8 +26,25 @@ fn main() {
|
|||||||
.add_systems(OnExit(GameState::MainMenu), despawn_main_menu)
|
.add_systems(OnExit(GameState::MainMenu), despawn_main_menu)
|
||||||
.add_observer(ui::button_hover_start)
|
.add_observer(ui::button_hover_start)
|
||||||
.add_observer(ui::button_hover_stop)
|
.add_observer(ui::button_hover_stop)
|
||||||
// TODO: System to operate buttons & other UI widgets
|
.add_systems(
|
||||||
// .add_systems(Update, )
|
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(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
@@ -42,9 +64,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Main game state indicator
|
/// Main game state indicator
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, States)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, States)]
|
||||||
enum GameState {
|
enum GameState {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
|
Connecting,
|
||||||
Playing,
|
Playing,
|
||||||
ConnectionDemo, // TODO: Remove this state.
|
ConnectionDemo, // TODO: Remove this state.
|
||||||
}
|
}
|
||||||
@@ -65,19 +88,50 @@ fn spawn_camera(mut commands: Commands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the scene
|
/// Initialize the scene
|
||||||
fn setup(
|
fn setup_game(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
window: Single<&Window>,
|
||||||
) {
|
) {
|
||||||
commands.spawn(Camera2d);
|
// ball
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
|
Ball,
|
||||||
Mesh2d(meshes.add(Circle::new(10.0))),
|
Mesh2d(meshes.add(Circle::new(10.0))),
|
||||||
MeshMaterial2d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
|
MeshMaterial2d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
|
||||||
Transform::default(),
|
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
|
/// ECS Component to hold the WebSocket. I guess there's going to be a magic
|
||||||
/// entity that controls the networking.
|
/// entity that controls the networking.
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@@ -101,7 +155,7 @@ enum WebSocketConnectionMessage {
|
|||||||
// TODO: Presumably a TeardownConnection, right?
|
// TODO: Presumably a TeardownConnection, right?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Debug, Error)]
|
||||||
enum ConnSetupError {
|
enum ConnSetupError {
|
||||||
#[error("IO")]
|
#[error("IO")]
|
||||||
Io(#[from] std::io::Error),
|
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`]
|
/// The task is self-removing, so we don't need to delete the [`WsSetupTask`]
|
||||||
/// component here.
|
/// 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 {
|
for mut task in &mut transform_tasks {
|
||||||
if let Some(result) = block_on(future::poll_once(&mut task.0)) {
|
if let Some(result) = block_on(future::poll_once(&mut task.0)) {
|
||||||
match result {
|
match result {
|
||||||
Ok(mut commands_queue) => {
|
Ok(mut commands_queue) => {
|
||||||
commands.append(&mut commands_queue);
|
commands.append(&mut commands_queue);
|
||||||
|
states.set(GameState::Playing);
|
||||||
}
|
}
|
||||||
Err(e) => info!("Connection failed. Err: {e:?}"),
|
Err(e) => info!("Connection failed. Err: {e:?}"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::GameState;
|
||||||
|
|
||||||
pub const BTN_BORDER_COLOR: Color = Color::WHITE;
|
pub const BTN_BORDER_COLOR: Color = Color::WHITE;
|
||||||
pub const BTN_BG_COLOR: Color = Color::BLACK;
|
pub const BTN_BG_COLOR: Color = Color::BLACK;
|
||||||
pub const BTN_BG_SELECTED: Color = bevy::prelude::Color::Srgba(DARK_GRAY);
|
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(),
|
TextShadow::default(),
|
||||||
));
|
));
|
||||||
let mut start_button = cmds.spawn(button_bundle("Start game"));
|
let mut start_button = cmds.spawn(button_bundle("Start game"));
|
||||||
start_button.observe(|_trigger: On<Pointer<Click>>| {
|
start_button.observe(
|
||||||
info!("The start button was pressed.");
|
|_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"));
|
let mut quit_button = cmds.spawn(button_bundle("Quit Game"));
|
||||||
quit_button.observe(
|
quit_button.observe(
|
||||||
|_trigger: On<Pointer<Click>>, mut messages: MessageWriter<AppExit>| {
|
|_trigger: On<Pointer<Click>>, mut messages: MessageWriter<AppExit>| {
|
||||||
info!("The quit button was pressed.");
|
|
||||||
// Quit the game if the quit button was pressed.
|
// Quit the game if the quit button was pressed.
|
||||||
messages.write(AppExit::Success);
|
messages.write(AppExit::Success);
|
||||||
},
|
},
|
||||||
@@ -43,8 +46,39 @@ pub fn spawn_main_menu(mut commands: Commands) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn despawn_main_menu(mut commands: Commands) {
|
/// Despawns the main menu (which is assumed to be the top-most node)
|
||||||
warn!("->> ui.rs: despawn_main_menu() is not implemented.");
|
///
|
||||||
|
/// 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.
|
/// The basic bundle for generic buttons.
|
||||||
@@ -77,7 +111,7 @@ pub fn button_hover_start(
|
|||||||
trigger: On<Pointer<Over>>,
|
trigger: On<Pointer<Over>>,
|
||||||
mut button_colors: Query<(&mut BackgroundColor, &mut BorderColor), With<Button>>,
|
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;
|
bg.0 = BTN_BG_SELECTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +120,7 @@ pub fn button_hover_stop(
|
|||||||
trigger: On<Pointer<Out>>,
|
trigger: On<Pointer<Out>>,
|
||||||
mut button_colors: Query<(&mut BackgroundColor, &mut BorderColor), With<Button>>,
|
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;
|
bg.0 = BTN_BG_COLOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user