generated from 2ndbeam/bevy-template
Compare commits
2 commits
7c386d4128
...
ffdb5d94a8
| Author | SHA1 | Date | |
|---|---|---|---|
| ffdb5d94a8 | |||
| 0a0eb14a6b |
12 changed files with 146 additions and 88 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -2422,7 +2422,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "expedition_demo"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_common_assets",
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use bevy::prelude::*;
|
||||
use leafwing_input_manager::prelude::*;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
Deserialize
|
||||
};
|
||||
|
||||
pub mod plugin;
|
||||
|
||||
#[derive(Actionlike, PartialEq, Eq, Hash, Debug, Clone, Reflect, Serialize, Deserialize)]
|
||||
#[reflect(PartialEq, Hash, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum InputAction {
|
||||
#[actionlike(Axis)]
|
||||
Move,
|
||||
|
|
@ -28,6 +29,7 @@ impl InputAction {
|
|||
}
|
||||
|
||||
#[derive(Actionlike, PartialEq, Eq, Hash, Debug, Clone, Reflect, Serialize, Deserialize)]
|
||||
#[reflect(PartialEq, Hash, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum UiAction {
|
||||
Rotate,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
use std::{any::{Any, TypeId}, collections::HashMap, hash::Hash, marker::PhantomData};
|
||||
use std::{
|
||||
any::{
|
||||
Any,
|
||||
TypeId,
|
||||
},
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use bevy::{prelude::*, reflect::GetTypeRegistration};
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
reflect::Reflectable,
|
||||
};
|
||||
use bevy_common_assets::toml::TomlAssetPlugin;
|
||||
use leafwing_input_manager::{Actionlike, plugin::InputManagerPlugin, prelude::*};
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use leafwing_input_manager::{
|
||||
Actionlike,
|
||||
plugin::InputManagerPlugin,
|
||||
prelude::*,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
de::DeserializeOwned,
|
||||
};
|
||||
|
||||
const INPUT_ASSET_EXTENSIONS: [&'static str; 1] = ["input.toml"];
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq, Reflect)]
|
||||
#[reflect(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct MultiInput {
|
||||
pub keyboard: Option<Vec<KeyCode>>,
|
||||
pub mouse: Option<Vec<MouseButton>>,
|
||||
|
|
@ -20,7 +40,8 @@ impl From<MultiInput> for InputKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Reflect)]
|
||||
#[reflect(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum InputKind {
|
||||
Button(MultiInput),
|
||||
|
|
@ -29,6 +50,7 @@ pub enum InputKind {
|
|||
}
|
||||
|
||||
#[derive(Default, Deref, DerefMut, Debug, Asset, Reflect, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[reflect(Debug, Clone, PartialEq)]
|
||||
pub struct InputAsset<Name>
|
||||
where Name: Sized + Hash + Eq + Reflect + TypePath + Actionlike {
|
||||
#[serde(flatten)]
|
||||
|
|
@ -205,17 +227,18 @@ impl<Name> From<InputMap<Name>> for InputAsset<Name>
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Deref)]
|
||||
pub struct InputAssetHandle<T: Sized + Hash + Eq + Reflect + TypePath + Actionlike> (Option<Handle<InputAsset<T>>>);
|
||||
#[derive(Resource, Debug, Deref, DerefMut, Reflect, Clone, PartialEq, Eq)]
|
||||
#[reflect(Resource, Debug, Clone, PartialEq)]
|
||||
pub struct InputAssetHandle<T: Sized + Hash + Eq + Reflectable + Actionlike> (Option<Handle<InputAsset<T>>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InputAssetPlugin<T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration> {
|
||||
pub struct InputAssetPlugin<T: Sized + Hash + Eq + Reflectable + Actionlike + DeserializeOwned> {
|
||||
_phantom: PhantomData<T>,
|
||||
extensions: &'static [&'static str],
|
||||
}
|
||||
|
||||
impl<T> InputAssetPlugin<T>
|
||||
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration {
|
||||
where T: Sized + Hash + Eq + Reflectable + Actionlike + DeserializeOwned {
|
||||
pub fn new(extensions: &'static [&'static str]) -> Self {
|
||||
Self {
|
||||
_phantom: PhantomData,
|
||||
|
|
@ -225,7 +248,7 @@ impl<T> InputAssetPlugin<T>
|
|||
}
|
||||
|
||||
impl<T> Default for InputAssetPlugin<T>
|
||||
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration {
|
||||
where T: Sized + Hash + Eq + Reflectable + Actionlike + DeserializeOwned {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_phantom: PhantomData,
|
||||
|
|
@ -235,12 +258,11 @@ impl<T> Default for InputAssetPlugin<T>
|
|||
}
|
||||
|
||||
impl<T> Plugin for InputAssetPlugin<T>
|
||||
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration
|
||||
{
|
||||
where T: Sized + Hash + Eq + Reflectable + Actionlike + DeserializeOwned {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins((
|
||||
InputManagerPlugin::<T>::default(),
|
||||
TomlAssetPlugin::<InputAsset<T>>::new(&self.extensions),
|
||||
InputManagerPlugin::<T>::default()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use std::mem::swap;
|
|||
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component, Clone, Debug, Reflect)]
|
||||
#[derive(Component, Clone, Debug, Reflect, PartialEq, Eq)]
|
||||
#[reflect(Component, Clone, Debug, PartialEq)]
|
||||
pub struct Item {
|
||||
pub size: UVec2,
|
||||
pub position: Option<UVec2>,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@ use bevy::prelude::*;
|
|||
|
||||
pub mod item;
|
||||
|
||||
#[derive(Component, Reflect, Default)]
|
||||
/// Marker that this inventory will show up when UI is built
|
||||
#[derive(Component, Reflect, Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[reflect(Component, Clone, Default, Debug, PartialEq)]
|
||||
pub struct ActiveInventory;
|
||||
|
||||
#[derive(Component, Reflect, Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[reflect(Component, Clone, Default, Debug, PartialEq)]
|
||||
pub struct Inventory {
|
||||
pub size: UVec2,
|
||||
}
|
||||
|
||||
/// Marker that this inventory will show up when UI is built
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct ActiveInventory;
|
||||
|
||||
impl Inventory {
|
||||
pub fn new(size: UVec2) -> Self {
|
||||
Self { size }
|
||||
|
|
@ -58,6 +60,23 @@ impl Inventory {
|
|||
self.can_fit(item_query, children.as_slice(), *size, queried_position)
|
||||
}
|
||||
|
||||
pub fn can_replace(
|
||||
&self,
|
||||
item_query: Query<&item::Item>,
|
||||
contained_items: &[Entity],
|
||||
replacable_item: Entity,
|
||||
queried_item: &item::Item,
|
||||
) -> bool {
|
||||
let Some(position) = &queried_item.position else {
|
||||
warn!("Trying to query rotated item without position");
|
||||
return false;
|
||||
};
|
||||
let children = contained_items.iter()
|
||||
.filter_map(|e| if e.ne(&&replacable_item) { Some(*e) } else { None })
|
||||
.collect::<Vec<Entity>>();
|
||||
self.can_fit(item_query, children.as_slice(), queried_item.size, *position)
|
||||
}
|
||||
|
||||
pub fn can_rotate(
|
||||
&self,
|
||||
item_query: Query<&item::Item>,
|
||||
|
|
@ -79,23 +98,6 @@ impl Inventory {
|
|||
self.can_fit(item_query, children.as_slice(), rotated_item.size, *position)
|
||||
}
|
||||
|
||||
pub fn can_replace(
|
||||
&self,
|
||||
item_query: Query<&item::Item>,
|
||||
contained_items: &[Entity],
|
||||
replacable_item: Entity,
|
||||
queried_item: &item::Item,
|
||||
) -> bool {
|
||||
let Some(position) = &queried_item.position else {
|
||||
warn!("Trying to query rotated item without position");
|
||||
return false;
|
||||
};
|
||||
let children = contained_items.iter()
|
||||
.filter_map(|e| if e.ne(&&replacable_item) { Some(*e) } else { None })
|
||||
.collect::<Vec<Entity>>();
|
||||
self.can_fit(item_query, children.as_slice(), queried_item.size, *position)
|
||||
}
|
||||
|
||||
fn find_free_space_inner(
|
||||
&self,
|
||||
item_query: Query<&item::Item>,
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Component)]
|
||||
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
|
||||
|
||||
#[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq, Eq)]
|
||||
#[reflect(Component, Clone, Default, Debug, PartialEq)]
|
||||
#[require(Sprite, InteractiveObject, Inventory)]
|
||||
pub struct Container;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,17 @@ use super::*;
|
|||
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
|
||||
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(Component, Clone, Copy, Reflect, PartialEq, Eq, Debug)]
|
||||
#[reflect(Component, Clone, Default, PartialEq, Debug)]
|
||||
#[require(Sprite, InteractiveObject)]
|
||||
pub struct Door(pub i8);
|
||||
|
||||
impl Default for Door {
|
||||
fn default() -> Self {
|
||||
Self(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_door_interact(
|
||||
event: On<InteractionEvent>,
|
||||
mut commands: Commands,
|
||||
|
|
|
|||
|
|
@ -4,16 +4,20 @@ pub mod container;
|
|||
pub mod door;
|
||||
pub mod systems;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
pub struct MayInteract;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
pub struct InteractiveObject;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
pub struct Locked;
|
||||
|
||||
#[derive(EntityEvent)]
|
||||
#[derive(EntityEvent, Reflect, Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[reflect(Event, Debug, PartialEq, Clone)]
|
||||
pub struct InteractionEvent {
|
||||
pub entity: Entity,
|
||||
}
|
||||
|
|
|
|||
66
src/lib.rs
66
src/lib.rs
|
|
@ -1,37 +1,32 @@
|
|||
use bevy::{
|
||||
prelude::*,
|
||||
ui_widgets::ScrollbarPlugin
|
||||
ui_widgets::ScrollbarPlugin,
|
||||
};
|
||||
use bevy_rapier2d::{
|
||||
prelude::*,
|
||||
rapier::prelude::IntegrationParameters
|
||||
rapier::prelude::IntegrationParameters,
|
||||
};
|
||||
|
||||
pub mod player;
|
||||
pub mod layout;
|
||||
pub mod input;
|
||||
pub mod inventory;
|
||||
pub mod ui;
|
||||
pub mod layout;
|
||||
pub mod player;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod ui;
|
||||
|
||||
pub const PIXELS_PER_METER: f32 = 16.0;
|
||||
|
||||
pub struct ExpeditionPlugin;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Reflect)]
|
||||
#[reflect(Clone, PartialEq, Debug, Default, Hash, State)]
|
||||
pub enum GameState {
|
||||
#[default]
|
||||
Running,
|
||||
Inventory,
|
||||
}
|
||||
|
||||
pub fn insert_entity_name(names: Query<(Entity, &mut Name), Added<Name>>) {
|
||||
for (entity, mut name) in names {
|
||||
name.mutate(|name| name.insert_str(0, format!("{entity}: ").as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
fn camera_bundle() -> impl Bundle {
|
||||
(
|
||||
Camera2d,
|
||||
|
|
@ -50,6 +45,12 @@ fn camera_bundle() -> impl Bundle {
|
|||
)
|
||||
}
|
||||
|
||||
fn insert_entity_name(names: Query<(Entity, &mut Name), Added<Name>>) {
|
||||
for (entity, mut name) in names {
|
||||
name.mutate(|name| name.insert_str(0, format!("{entity}: ").as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_global(mut commands: Commands) {
|
||||
commands.spawn(camera_bundle());
|
||||
commands.spawn(ui::UiRoot::new());
|
||||
|
|
@ -57,34 +58,39 @@ fn setup_global(mut commands: Commands) {
|
|||
|
||||
impl Plugin for ExpeditionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let rapier_init = RapierContextInitialization::InitializeDefaultRapierContext {
|
||||
integration_parameters: IntegrationParameters {
|
||||
length_unit: PIXELS_PER_METER,
|
||||
..default()
|
||||
},
|
||||
rapier_configuration: RapierConfiguration {
|
||||
gravity: Vec2::ZERO,
|
||||
physics_pipeline_active: true,
|
||||
scaled_shape_subdivision: 10,
|
||||
force_update_from_transform_changes: false,
|
||||
},
|
||||
};
|
||||
app.add_plugins((
|
||||
RapierDebugRenderPlugin::default(),
|
||||
RapierPhysicsPlugin::<()>::default()
|
||||
.with_custom_initialization(rapier_init),
|
||||
ScrollbarPlugin,
|
||||
input::plugin::InputAssetPlugin::<input::InputAction>::default(),
|
||||
input::plugin::InputAssetPlugin::<input::UiAction>::default(),
|
||||
ScrollbarPlugin,
|
||||
RapierPhysicsPlugin::<()>::default()
|
||||
.with_custom_initialization(RapierContextInitialization::InitializeDefaultRapierContext {
|
||||
integration_parameters: IntegrationParameters {
|
||||
length_unit: PIXELS_PER_METER,
|
||||
..default()
|
||||
},
|
||||
rapier_configuration: RapierConfiguration {
|
||||
gravity: Vec2::ZERO,
|
||||
physics_pipeline_active: true,
|
||||
scaled_shape_subdivision: 10,
|
||||
force_update_from_transform_changes: false,
|
||||
},
|
||||
}),
|
||||
RapierDebugRenderPlugin::default(),
|
||||
))
|
||||
.init_state::<GameState>()
|
||||
.insert_resource(ui::WindowSize::default())
|
||||
.add_systems(Startup, (player::systems::setup_player, setup_global, layout::systems::setup_world))
|
||||
.add_systems(Startup, (
|
||||
setup_global,
|
||||
layout::systems::setup_world,
|
||||
player::systems::setup_player,
|
||||
))
|
||||
.add_systems(Update, (
|
||||
insert_entity_name,
|
||||
layout::systems::detect_interact_collisions,
|
||||
player::systems::handle_input,
|
||||
ui::update_window_size,
|
||||
ui::handle_input,
|
||||
insert_entity_name,
|
||||
layout::systems::detect_interact_collisions,
|
||||
))
|
||||
.add_systems(OnEnter(GameState::Inventory), ui::inventory::systems::setup_ui_inventory)
|
||||
.add_systems(OnExit(GameState::Inventory), ui::inventory::systems::clear_ui_inventory)
|
||||
|
|
|
|||
|
|
@ -9,18 +9,23 @@ use crate::{
|
|||
|
||||
pub mod systems;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Clone, Copy, Reflect, PartialEq, Debug)]
|
||||
#[reflect(Component, Clone, Default, PartialEq, Debug)]
|
||||
pub struct Player {
|
||||
// px/s
|
||||
speed: f32,
|
||||
}
|
||||
|
||||
impl Default for Player {
|
||||
fn default() -> Self {
|
||||
Self { speed: PIXELS_PER_METER * 0.8 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
|
||||
let image = asset_server.load("sprites/player/player.png");
|
||||
(
|
||||
Player {
|
||||
speed: PIXELS_PER_METER * 0.8,
|
||||
},
|
||||
Player::default(),
|
||||
Sprite::from_image(image),
|
||||
Transform::from_xyz(0f32, 0f32, 1f32),
|
||||
Action::default_input_map(),
|
||||
|
|
|
|||
|
|
@ -14,29 +14,36 @@ pub mod bundles;
|
|||
pub mod observers;
|
||||
pub mod systems;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventoryManager;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Clone)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventory(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiInventorySlot(pub UVec2);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Clone)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiItem(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
pub struct HoveredItem;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Default, Clone)]
|
||||
pub struct HoveredSlot;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Debug, PartialEq, Eq, Clone, Reflect)]
|
||||
#[reflect(Component, Debug, PartialEq, Clone)]
|
||||
pub struct DraggedItem(pub Item, pub UVec2);
|
||||
|
||||
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ use crate::input::UiAction;
|
|||
|
||||
pub mod inventory;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Reflect, Debug, Default, PartialEq, Eq, Clone, Copy)]
|
||||
#[require(Node)]
|
||||
#[reflect(Component, Debug, Default, PartialEq, Clone)]
|
||||
pub struct UiRoot;
|
||||
|
||||
#[derive(Resource, Deref, DerefMut, Default)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue