Skeleton of an editor. Just a window with solid toggling.
parent
c31801e876
commit
5c71f55363
@ -0,0 +1,5 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub(crate) fn any_component_added<T: Component>(q: Query<Entity, Added<T>>) -> bool {
|
||||||
|
!q.is_empty()
|
||||||
|
}
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub(crate) struct EditorPlugin;
|
||||||
|
|
||||||
|
impl Plugin for EditorPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.init_state::<EditorState>()
|
||||||
|
.add_systems(Startup, init_editor)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
toggle_editor_state.run_if(input_just_pressed(KeyCode::F3)),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
OnTransition {
|
||||||
|
from: EditorState::Open,
|
||||||
|
to: EditorState::Closed,
|
||||||
|
},
|
||||||
|
toggle_editor_window,
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
OnTransition {
|
||||||
|
from: EditorState::Closed,
|
||||||
|
to: EditorState::Open,
|
||||||
|
},
|
||||||
|
toggle_editor_window,
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
handle_window_close.run_if(on_event::<WindowCloseRequested>()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracking the open/closed state of the editor
|
||||||
|
#[derive(States, Debug, Clone, PartialEq, Eq, Hash, Default, Component)]
|
||||||
|
enum EditorState {
|
||||||
|
/// The editor is closed => the editor window is disabled
|
||||||
|
#[default]
|
||||||
|
Closed,
|
||||||
|
/// The editor is open => the editor window is visible
|
||||||
|
Open,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Not for &EditorState {
|
||||||
|
type Output = EditorState;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
EditorState::Open => EditorState::Closed,
|
||||||
|
EditorState::Closed => EditorState::Open,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<&EditorState> for bool {
|
||||||
|
fn from(value: &EditorState) -> bool {
|
||||||
|
match value {
|
||||||
|
EditorState::Open => true,
|
||||||
|
EditorState::Closed => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tag component for editor entities
|
||||||
|
#[derive(Component)]
|
||||||
|
struct EditorTag;
|
||||||
|
|
||||||
|
/// At startup initialize some editor components
|
||||||
|
fn init_editor(mut commands: Commands) {
|
||||||
|
// Editor window
|
||||||
|
let editor_window = commands
|
||||||
|
.spawn((
|
||||||
|
EditorTag,
|
||||||
|
Window {
|
||||||
|
visible: false,
|
||||||
|
title: "Editor".into(),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
// Editor camera
|
||||||
|
let editor_camera = commands
|
||||||
|
.spawn((
|
||||||
|
EditorTag,
|
||||||
|
Camera3dBundle {
|
||||||
|
camera: Camera {
|
||||||
|
target: RenderTarget::Window(WindowRef::Entity(editor_window)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
// Editor UI elements
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
EditorTag,
|
||||||
|
TargetCamera(editor_camera),
|
||||||
|
NodeBundle { ..default() },
|
||||||
|
))
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn((
|
||||||
|
EditorTag,
|
||||||
|
TextBundle::from_section(
|
||||||
|
"Welcome to the editor",
|
||||||
|
TextStyle {
|
||||||
|
color: Color::WHITE,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_editor_state(
|
||||||
|
curr_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 curr_state.get() {
|
||||||
|
EditorState::Open => next_state.set(EditorState::Closed),
|
||||||
|
EditorState::Closed => next_state.set(EditorState::Open),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*catch = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_editor_window(
|
||||||
|
state: Res<State<EditorState>>,
|
||||||
|
mut windows: Query<&mut Window, With<EditorTag>>,
|
||||||
|
mut cameras: Query<&mut Camera, With<EditorTag>>,
|
||||||
|
) {
|
||||||
|
let curr = state.get().into();
|
||||||
|
cameras.iter_mut().for_each(|mut camera| {
|
||||||
|
camera.is_active = curr;
|
||||||
|
});
|
||||||
|
windows.iter_mut().for_each(|mut window| {
|
||||||
|
window.visible = curr;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles window close requests which are only weird because of the editor
|
||||||
|
/// When the editor window closes, just make it invisible and change the editor state to "Closed".
|
||||||
|
fn handle_window_close(
|
||||||
|
mut events: EventReader<WindowCloseRequested>,
|
||||||
|
editor_windows: Query<Entity, With<EditorTag>>,
|
||||||
|
mut editor_state: ResMut<NextState<EditorState>>,
|
||||||
|
) {
|
||||||
|
events
|
||||||
|
.read()
|
||||||
|
// Filter events down to jus those affecting the editor window
|
||||||
|
.filter_map(|WindowCloseRequested { window }| editor_windows.get(*window).ok())
|
||||||
|
// For each related close event, send out a "Close the editor" state transition
|
||||||
|
.for_each(|_| {
|
||||||
|
editor_state.set(EditorState::Closed);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,14 +1,26 @@
|
|||||||
|
/// ECS scheduling `run_if` conditions
|
||||||
|
pub(crate) mod conditions;
|
||||||
|
/// Editor: debugging and run-time modifications to the game
|
||||||
|
pub(crate) mod editor;
|
||||||
|
/// Menu: Main and otherwise
|
||||||
|
pub(crate) mod menu;
|
||||||
/// Helper module containing common imports across the project
|
/// Helper module containing common imports across the project
|
||||||
pub(crate) mod prelude;
|
pub(crate) mod prelude;
|
||||||
|
/// Window handling
|
||||||
/// Menu plugin, state, systems
|
pub(crate) mod window;
|
||||||
pub(crate) mod menu;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
// handled in crate::window::handle_window_close and crate::editor::handle_window_close
|
||||||
|
close_when_requested: false,
|
||||||
|
exit_condition: bevy::window::ExitCondition::OnPrimaryClosed,
|
||||||
|
..default()
|
||||||
|
}))
|
||||||
.add_plugins(menu::MenuPlugin)
|
.add_plugins(menu::MenuPlugin)
|
||||||
|
.add_plugins(editor::EditorPlugin)
|
||||||
|
.add_plugins(window::WindowPlugin)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,9 @@
|
|||||||
|
pub(crate) use bevy::app::AppExit;
|
||||||
|
pub(crate) use bevy::input::common_conditions::input_just_pressed;
|
||||||
pub(crate) use bevy::prelude::*;
|
pub(crate) use bevy::prelude::*;
|
||||||
|
pub(crate) use bevy::render::camera::RenderTarget;
|
||||||
|
pub(crate) use bevy::window::PrimaryWindow;
|
||||||
|
pub(crate) use bevy::window::WindowCloseRequested;
|
||||||
|
pub(crate) use bevy::window::WindowRef;
|
||||||
|
|
||||||
|
pub(crate) use crate::conditions::*;
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub(crate) struct WindowPlugin;
|
||||||
|
|
||||||
|
impl Plugin for WindowPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
setup_primary.run_if(any_component_added::<PrimaryWindow>),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
handle_window_close.run_if(on_event::<WindowCloseRequested>()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_primary(mut events: Query<&mut Window, Added<PrimaryWindow>>) {
|
||||||
|
events.iter_mut().for_each(|mut window| {
|
||||||
|
window.title = "Acts of Gods".into();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles window close requests which are only weird because of the editor
|
||||||
|
/// When the primary window closes shut down the game.
|
||||||
|
fn handle_window_close(
|
||||||
|
mut events: EventReader<WindowCloseRequested>,
|
||||||
|
primary: Query<Entity, With<PrimaryWindow>>,
|
||||||
|
mut exit: EventWriter<AppExit>,
|
||||||
|
) {
|
||||||
|
events
|
||||||
|
.read()
|
||||||
|
// FilterMap this entity to the primary window
|
||||||
|
// Meaning if we get Some(entity) the event was for the primary
|
||||||
|
// If we get None it was for a different window so we skip the for_each
|
||||||
|
.filter_map(|WindowCloseRequested { window }| primary.get(*window).ok())
|
||||||
|
// If this was the primary window, send an AppExit
|
||||||
|
.for_each(|_| {
|
||||||
|
exit.send(AppExit);
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue