Compare commits

..

No commits in common. '1e42519a312d2a5ba28ee337933263906e4c2064' and 'ba19b6342ea93182a5cb84be53fe631df3b03a24' have entirely different histories.

@ -1,23 +1,22 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use bevy::{ use bevy::{
input::common_conditions::input_just_pressed, prelude::*, render::{ prelude::*,
render::{
camera::RenderTarget, camera::RenderTarget,
render_resource::{ render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
}, },
} },
}; };
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_systems(Startup, add_texture) .add_systems(Startup, add_texture)
.add_systems( .add_systems(Update, rotate_mesh.run_if(|keys: Res<ButtonInput<KeyCode>>| -> bool {
Update, keys.pressed(KeyCode::Space)
rotate_mesh }))
.run_if(input_just_pressed(KeyCode::Space)),
)
.run(); .run();
} }
@ -84,32 +83,30 @@ fn add_texture(
["Up", "Down", "Left", "Right", "Top", "Bottom"] ["Up", "Down", "Left", "Right", "Top", "Bottom"]
.into_iter() .into_iter()
.for_each(|text| { .for_each(|text| {
parent parent.spawn(NodeBundle {
.spawn(NodeBundle { style: Style {
style: Style { height: Val::Percent(100.0 / 6.0),
height: Val::Percent(100.0 / 6.0), width: Val::Percent(100.0),
width: Val::Percent(100.0), justify_content: JustifyContent::Center,
justify_content: JustifyContent::Center, justify_items: JustifyItems::Center,
justify_items: JustifyItems::Center, align_items: AlignItems::Center,
align_items: AlignItems::Center, align_content: AlignContent::Center,
align_content: AlignContent::Center, ..default()
..default() },
}, ..default()
}).with_children(|parent| {
parent.spawn(TextBundle {
text: Text::from_section(
text,
TextStyle {
color: Color::BLACK,
font_size: 64.0,
..default()
},
),
..default() ..default()
})
.with_children(|parent| {
parent.spawn(TextBundle {
text: Text::from_section(
text,
TextStyle {
color: Color::BLACK,
font_size: 64.0,
..default()
},
),
..default()
});
}); });
});
}); });
}); });
@ -133,6 +130,31 @@ fn add_texture(
} }
{ {
let cube_size = 4.0;
let a = 1.0 / 6.0;
let b = 2.0 * a;
let c = 3.0 * a;
let d = 4.0 * a;
let e = 5.0 * a;
let f = 1.0;
let cuboid = Mesh::from(Cuboid::new(cube_size, cube_size, cube_size))
.with_inserted_attribute(
Mesh::ATTRIBUTE_UV_0,
vec![
// Top side
[1.0, 0.0], [0.0, 0.0], [0.0, a], [1.0, a],
// Bottom side
[1.0, a], [0.0, a], [0.0, b], [1.0, b],
// Right side
[1.0, b], [0.0, b], [0.0, c], [1.0, c],
// Left side
[1.0, c], [0.0, c], [0.0, d], [1.0, d],
// Back side
[1.0, d], [0.0, d], [0.0, e], [1.0, e],
// Front side
[1.0, e], [0.0, e], [0.0, f], [1.0, f],
]);
let mesh = meshes.add(cuboid);
let material = materials.add(StandardMaterial { let material = materials.add(StandardMaterial {
base_color_texture: Some(image_handle), base_color_texture: Some(image_handle),
..default() ..default()
@ -156,10 +178,13 @@ fn add_texture(
} }
} }
fn rotate_mesh(mut query: Query<&mut Transform, With<Handle<Mesh>>>, time: Res<Time>) { fn rotate_mesh(
mut query: Query<&mut Transform, With<Handle<Mesh>>>,
time: Res<Time>,
) {
query.iter_mut().for_each(|mut t| { query.iter_mut().for_each(|mut t| {
t.rotate_x(time.delta_seconds() * 0.5); t.rotate_x(time.delta_seconds() * 0.5);
t.rotate_y(time.delta_seconds() * 0.5); t.rotate_y(time.delta_seconds() * 0.5);
t.rotate_z(time.delta_seconds() * 0.5); t.rotate_z(time.delta_seconds() * 0.5);
}); });
} }

@ -1,89 +0,0 @@
use bevy::input::mouse::MouseButtonInput;
use crate::prelude::*;
/// Menu Plugin; empty struct for Plugin impl
pub(crate) struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, editor_fly_camera);
}
}
#[derive(Component)]
pub(crate) struct FlyCamera;
/// Fly camera system for moving around like a drone
/// TODO: Only if key is pressed!
fn editor_fly_camera(
mut cameras: Query<(&Camera, &mut Transform), With<FlyCamera>>,
windows: Query<&Window>,
primary_window: Query<Entity, With<PrimaryWindow>>,
keys: Res<ButtonInput<KeyCode>>,
mouse: Res<ButtonInput<MouseButton>>,
mut cursor_events: EventReader<CursorMoved>,
time: Res<Time>,
) {
let any_keys_pressed = keys.any_pressed([ KeyCode::KeyW, KeyCode::KeyS, KeyCode::KeyA, KeyCode::KeyD, KeyCode::KeyQ, KeyCode::KeyE ]);
let cursor_movement = cursor_events.len() > 0;
(any_keys_pressed || cursor_movement).then(|| {
// Iterate over all cameras
cameras.iter_mut().for_each(|(c, mut t)| {
// Determine which window this camera is attached to
let target_window = match c.target {
RenderTarget::Window(wr) => match wr {
WindowRef::Entity(e) => Some(e),
WindowRef::Primary => Some(primary_window.get_single().unwrap()),
},
_ => None,
};
let window = windows.get(target_window.unwrap()).unwrap();
// If the target window is focused
window.focused.then(|| {
let move_speed = 4.0;
let mut delta = Vec3::ZERO;
if keys.pressed(KeyCode::KeyW) {
delta += t.forward() * move_speed * time.delta_seconds()
}
if keys.pressed(KeyCode::KeyS) {
delta += t.back() * move_speed * time.delta_seconds()
}
if keys.pressed(KeyCode::KeyA) {
delta += t.left() * move_speed * time.delta_seconds()
}
if keys.pressed(KeyCode::KeyD) {
delta += t.right() * move_speed * time.delta_seconds()
}
if keys.pressed(KeyCode::KeyE) {
delta += Vec3::Y * move_speed * time.delta_seconds()
}
if keys.pressed(KeyCode::KeyQ) {
delta += Vec3::NEG_Y * move_speed * time.delta_seconds()
}
t.translation += delta;
});
if mouse.pressed(MouseButton::Middle) {
cursor_events.read().filter_map(|CursorMoved { delta, window, .. }| {
(*window == target_window.unwrap()).then_some(delta)
}).for_each(|delta| {
if let Some(Vec2 { x, y }) = delta {
// Cribbing from bevy_flycam
// Link: https://github.com/sburris0/bevy_flycam/blob/baffe50e0961ad1491d467fa6ab5551f9f21db8f/src/lib.rs#L145-L151
let (mut yaw, mut pitch, _) = t.rotation.to_euler(EulerRot::YXZ);
let window_scale = window.height().min(window.width());
let sensitivity = 0.00012;
pitch -= (sensitivity * y * window_scale).to_radians();
yaw -= (sensitivity * x * window_scale).to_radians();
t.rotation = Quat::from_axis_angle(Vec3::Y, yaw) * Quat::from_axis_angle(Vec3::X, pitch);
}
});
} else {
cursor_events.clear();
}
});
});
}

@ -1,121 +0,0 @@
use crate::prelude::*;
/// Menu Plugin; empty struct for Plugin impl
pub(crate) struct EditorPlugin;
impl Plugin for EditorPlugin {
fn build(&self, app: &mut App) {
app.init_state::<EditorState>();
app.add_systems(Startup, init_editor);
app.add_systems(Update, toggle_editor.run_if(input_just_pressed(KeyCode::F3)));
app.add_systems(OnEnter(EditorState::Open), open_editor);
app.add_systems(OnExit(EditorState::Open), close_editor);
app.add_systems(Update, origin_directions.run_if(in_state(EditorState::Open)));
app.add_systems(Update, world_plane.run_if(in_state(EditorState::Open)));
}
}
/// State tracking if the editor is open
#[derive(States, Debug, Clone, PartialEq, Eq, Hash, Default, Component)]
enum EditorState {
#[default]
Closed,
Open,
}
#[derive(Component)]
struct Editor;
/// Spawns all base editor entities including window, camera, and UI elements
fn init_editor(
mut commands: Commands
) {
// Spawn root editor entity hierarchy
commands.spawn(SpatialBundle { ..default() })
.with_children(|parent| {
let editor_window = parent.spawn((
Editor,
Window {
title: "Editor".into(),
name: Some("Editor".into()),
visible: false,
..default()
},
)).id();
// Spawn editor camera
let _editor_camera = parent.spawn((
Editor,
FlyCamera,
Camera3dBundle {
camera: Camera {
target: RenderTarget::Window(WindowRef::Entity(editor_window)),
..default()
},
transform: Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
)).id();
});
}
fn open_editor(
mut ws: Query<&mut Window, With<Editor>>,
) {
ws.iter_mut().for_each(|mut w| {
w.visible = true;
});
}
fn close_editor(
mut ws: Query<&mut Window, With<Editor>>,
) {
ws.iter_mut().for_each(|mut w| {
w.visible = false;
});
}
fn toggle_editor(
state: Res<State<EditorState>>,
mut next_state: ResMut<NextState<EditorState>>,
keys: Res<ButtonInput<KeyCode>>,
mut catch: Local<bool>,
) {
if keys.pressed(KeyCode::F3) && !*catch {
*catch = true;
match state.get() {
EditorState::Open => next_state.set(EditorState::Closed),
EditorState::Closed => next_state.set(EditorState::Open),
}
} else {
*catch = false;
}
}
fn origin_directions(
mut gizmos: Gizmos
) {
gizmos.arrow(Vec3::ZERO, Vec3::X, Color::RED);
gizmos.arrow(Vec3::ZERO, Vec3::Y, Color::GREEN);
gizmos.arrow(Vec3::ZERO, Vec3::Z, Color::BLUE);
}
fn world_plane(
mut gizmos: Gizmos
) {
(-10..=10).into_iter().for_each(|x| {
(-10..=10).into_iter().for_each(|z| {
{
let start = Vec3::new(x as f32, 0.0, -10.0);
let end = Vec3::new(x as f32, 0.0, 10.0);
gizmos.line(start, end, Color::GRAY);
}
{
let start = Vec3::new(-10.0, 0.0, z as f32);
let end = Vec3::new(10.0, 0.0, z as f32);
gizmos.line(start, end, Color::GRAY);
}
});
});
}

@ -7,12 +7,12 @@ impl Plugin for DicePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<DiceAction>(); app.add_event::<DiceAction>();
app.add_systems(Startup, init_dice_ui); app.add_systems(Startup, init_dice_ui);
app.add_systems(Startup, init_dice_cuboid.before(init_dice));
app.add_systems(Startup, init_dice); app.add_systems(Startup, init_dice);
app.add_systems( app.add_systems(
Update, Update,
button_emit_event::<DiceAction>.run_if(any_component_changed::<Interaction>), button_emit_event::<DiceAction>.run_if(any_component_changed::<Interaction>),
); );
app.add_systems(Update, draw_die.run_if(any_component_changed::<Die>));
app.add_systems(Update, roll_die.run_if(on_event::<DiceAction>())); app.add_systems(Update, roll_die.run_if(on_event::<DiceAction>()));
app.add_systems(Update, move_die.run_if(on_event::<KeyboardInput>())); app.add_systems(Update, move_die.run_if(on_event::<KeyboardInput>()));
} }
@ -23,36 +23,6 @@ enum DiceAction {
Roll, Roll,
} }
#[rustfmt::skip]
fn init_dice_cuboid(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
// Store handle to mesh for later use
let cube_size = 1.0;
let a = 1.0 / 6.0;
let b = 2.0 * a;
let c = 3.0 * a;
let d = 4.0 * a;
let e = 5.0 * a;
let f = 1.0;
let cuboid = Mesh::from(Cuboid::new(cube_size, cube_size, cube_size)).with_inserted_attribute(
Mesh::ATTRIBUTE_UV_0,
vec![
// Top side
[1.0, 0.0], [0.0, 0.0], [0.0, a], [1.0, a],
// Bottom side
[1.0, a], [0.0, a], [0.0, b], [1.0, b],
// Right side
[1.0, b], [0.0, b], [0.0, c], [1.0, c],
// Left side
[1.0, c], [0.0, c], [0.0, d], [1.0, d],
// Back side
[1.0, d], [0.0, d], [0.0, e], [1.0, e],
// Front side
[1.0, e], [0.0, e], [0.0, f], [1.0, f],
],
);
commands.insert_resource(DiceCuboid { mesh: meshes.add(cuboid) });
}
/// Create UI for the Dice game at startup /// Create UI for the Dice game at startup
fn init_dice_ui(mut commands: Commands) { fn init_dice_ui(mut commands: Commands) {
commands commands
@ -97,7 +67,11 @@ fn init_dice_ui(mut commands: Commands) {
}); });
} }
fn init_dice(mut commands: Commands) { fn init_dice(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
mut commands: Commands,
) {
[ [
["a", "b", "c", "d", "e", "f"], ["a", "b", "c", "d", "e", "f"],
["g", "h", "i", "j", "k", "l"], ["g", "h", "i", "j", "k", "l"],
@ -105,18 +79,46 @@ fn init_dice(mut commands: Commands) {
] ]
.into_iter() .into_iter()
.enumerate() .enumerate()
.for_each(|(_idx, set)| { .for_each(|(idx, set)| {
commands commands
.spawn(( .spawn((
GameChoice::Dice, GameChoice::Dice,
MenuState::Closed, MenuState::Closed,
Die::new(set),
PickableBundle { ..default() }, PickableBundle { ..default() },
On::<Pointer<Drag>>::target_component_mut::<Transform>(|m, transform| { On::<Pointer<Drag>>::target_component_mut::<Transform>(|m, transform| {
transform.translation.x += m.delta.x; transform.translation.x += m.delta.x;
transform.translation.y -= m.delta.y; transform.translation.y -= m.delta.y;
}), }),
)) ))
.add(Die::new(set)); .insert(MaterialMesh2dBundle {
mesh: meshes
.add(Rectangle {
half_size: Vec2::splat(32.0),
})
.into(),
material: materials.add(Color::PINK),
..default()
})
.insert(Text2dBundle {
text: Text::from_section(
"",
TextStyle {
color: Color::BLACK,
font_size: 32.0,
..default()
},
),
..default()
})
.insert(Transform::from_xyz(idx as f32 * 100.0, 100.0, 0.0));
});
}
fn draw_die(mut q: Query<(&Die, &mut Text), Changed<Die>>) {
q.iter_mut().for_each(|(d, mut t)| {
info!("Dice currently reads {:?}", d.get());
t.sections[0].value.replace_range(.., d.get());
}); });
} }
@ -157,6 +159,7 @@ fn move_die(
} }
_ => (), _ => (),
} }
info!("Moved die {:?}", t.translation);
}); });
} }
_ => (), _ => (),
@ -172,123 +175,6 @@ struct Die {
top: usize, top: usize,
} }
#[derive(Resource)]
struct DiceCuboid {
mesh: Handle<Mesh>,
}
impl EntityCommand for Die {
fn apply(self, id: Entity, world: &mut World) {
info!("Applying entity command for die");
// Image we will render our texture to
let image = {
let size = Extent3d {
width: 256,
height: 256 * 6,
..default()
};
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8UnormSrgb,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
..default()
};
image.resize(size);
image
};
// Create image
let image_handle = world.resource_mut::<Assets<Image>>().add(image);
// Camera capturing a hidden scene rendered to a texture
let texture_camera = world
.spawn(Camera2dBundle {
camera: Camera {
order: -1,
target: RenderTarget::Image(image_handle.clone()),
..default()
},
..default()
})
.id();
// Scene being rendered to a texture
world
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
background_color: BackgroundColor(Color::GOLD),
..default()
},
TargetCamera(texture_camera),
))
.with_children(|parent| {
self.sides.iter().for_each(|text| {
parent
.spawn(NodeBundle {
style: Style {
height: Val::Percent(100.0 / 6.0),
width: Val::Percent(100.0),
justify_content: JustifyContent::Center,
justify_items: JustifyItems::Center,
align_items: AlignItems::Center,
align_content: AlignContent::Center,
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle {
text: Text::from_section(
*text,
TextStyle {
color: Color::BLACK,
font_size: 64.0,
..default()
},
),
..default()
});
});
});
});
// Material used for this object
let this_material =
world
.resource_mut::<Assets<StandardMaterial>>()
.add(StandardMaterial {
base_color_texture: Some(image_handle),
..default()
});
let pbr_bundle = PbrBundle {
mesh: world.resource::<DiceCuboid>().mesh.clone(),
material: this_material.clone(),
..default()
};
world.entity_mut(id).insert((self, pbr_bundle.clone()));
}
}
impl Die { impl Die {
/// Initialize all dice values /// Initialize all dice values
fn new(sides: [&'static str; 6]) -> Die { fn new(sides: [&'static str; 6]) -> Die {

@ -31,12 +31,11 @@ pub(crate) enum GameChoice {
/// Main game camera /// Main game camera
fn init_camera(mut commands: Commands) { fn init_camera(mut commands: Commands) {
commands.spawn(Camera3dBundle { commands.spawn(Camera2dBundle {
camera: Camera { camera: Camera {
clear_color: ClearColorConfig::Custom(Color::ALICE_BLUE), clear_color: ClearColorConfig::Custom(Color::WHITE),
..default() ..default()
}, },
transform: Transform::from_xyz(10.0, 10.0, 10.0),
..default() ..default()
}); });
} }

@ -16,36 +16,17 @@ pub(crate) mod ecs;
/// Imports used across the project /// Imports used across the project
pub(crate) mod prelude; pub(crate) mod prelude;
/// Different game modes
pub(crate) mod game; pub(crate) mod game;
/// Debugger/Editor for inspecting the game
pub(crate) mod editor;
/// Camera control, mostly used in debug editor
pub(crate) mod camera;
use crate::prelude::*; use crate::prelude::*;
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
app.add_plugins(bevy::DefaultPlugins app.add_plugins(bevy::DefaultPlugins.set(low_latency_window_plugin()));
.set(low_latency_window_plugin())
.set(WindowPlugin {
exit_condition: ExitCondition::OnPrimaryClosed,
close_when_requested: false,
..default()
})
);
app.add_plugins(bevy_mod_picking::DefaultPickingPlugins); app.add_plugins(bevy_mod_picking::DefaultPickingPlugins);
app.add_plugins(menu::MenuPlugin); app.add_plugins(menu::MenuPlugin);
app.add_plugins(ui::UiPlugin); app.add_plugins(ui::UiPlugin);
app.add_plugins(game::GamePlugin); app.add_plugins(game::GamePlugin);
app.add_plugins(editor::EditorPlugin);
app.add_plugins(camera::CameraPlugin);
app.add_systems(Update,
handle_window_close.run_if(on_event::<WindowCloseRequested>())
);
app.run(); app.run();
} }
@ -62,25 +43,3 @@ pub(crate) fn manage_visibility<SC: States + Component>(
} }
}); });
} }
/// When the primary window is requested to close, do so (exiting the game).
/// When a non-primary window is closed, just hide it instead of actually closing
fn handle_window_close(
mut events: EventReader<WindowCloseRequested>,
primary: Query<Entity, With<PrimaryWindow>>,
mut secondary: Query<&mut Window, Without<PrimaryWindow>>,
mut commands: Commands,
) {
events.read().for_each(|WindowCloseRequested { window }| {
if primary.contains(*window) {
commands.entity(*window).remove::<Window>();
} else {
secondary
.get_mut(*window)
.iter_mut()
.for_each(|w| {
w.visible = false;
});
}
});
}

@ -1,18 +1,11 @@
pub(crate) use std::fmt::Debug; pub(crate) use std::fmt::Debug;
/// Bevy imports
pub(crate) use bevy::ecs::system::EntityCommand; pub(crate) use bevy::ecs::system::EntityCommand;
pub(crate) use bevy::input::keyboard::KeyboardInput; pub(crate) use bevy::input::keyboard::KeyboardInput;
pub(crate) use bevy::input::ButtonState; pub(crate) use bevy::input::ButtonState;
/// Bevy imports
pub(crate) use bevy::prelude::*; pub(crate) use bevy::prelude::*;
pub(crate) use bevy::render::camera::RenderTarget; pub(crate) use bevy::sprite::MaterialMesh2dBundle;
pub(crate) use bevy::render::render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
};
pub(crate) use bevy::window::WindowRef;
pub(crate) use bevy::window::{PrimaryWindow, WindowCloseRequested};
pub(crate) use bevy::window::ExitCondition;
pub(crate) use bevy::input::common_conditions::input_just_pressed;
/// Bevy Plugins /// Bevy Plugins
pub(crate) use bevy_mod_picking::prelude::*; pub(crate) use bevy_mod_picking::prelude::*;
@ -30,4 +23,3 @@ pub(crate) use crate::ui::style::UiStyle;
pub(crate) use crate::ui::title::UiTitle; pub(crate) use crate::ui::title::UiTitle;
pub(crate) use crate::ui::EmitEvent; pub(crate) use crate::ui::EmitEvent;
pub(crate) use crate::ui::SetState; pub(crate) use crate::ui::SetState;
pub(crate) use crate::camera::FlyCamera;

@ -1 +0,0 @@
// TODO: window management in this file
Loading…
Cancel
Save