idanarye / bevy-yoleck Goto Github PK
View Code? Open in Web Editor NEWYour Own Level Editor Creation Kit
License: Other
Your Own Level Editor Creation Kit
License: Other
Does it use bevy scenes? ron? Something else? Would be nice with a brief description of how it works under the hood in the readme.
Currently, if you load the level index like shown in the docs.rs example, it seems as though the level_index_assets.get
method breaks.
Loading this plugin panics with "Failed to load level" when in the Loading
state
// SYSTEMS
pub fn load_level(
level_index_assets: Res<Assets<YoleckLevelIndex>>,
asset_server: Res<AssetServer>,
mut yoleck_loading_cmd: ResMut<YoleckLoadingCommand>,
current_level: Res<CurrentLevel>,
mut state: ResMut<NextState<GameState>>,
) {
if let Some(level) = current_level.0 {
bevy::log::debug!("LOADING LEVEL: {level}");
let level_index_handle: Handle<YoleckLevelIndex> = asset_server.load("levels/index.yoli");
let Some(level_index) = level_index_assets.get(&level_index_handle) else { panic!("Failed to load level") };
let level_handle: Handle<YoleckRawLevel> = asset_server.load(
format!("levels/{}", level_index[level].filename)
);
*yoleck_loading_cmd = YoleckLoadingCommand::FromAsset(level_handle);
state.set(GameState::InGame);
}
}
// PLUGIN
pub struct Plugin;
impl bevy::app::Plugin for Plugin {
fn build(&self, app: &mut App) {
app
.insert_resource(CurrentLevel(Some(1)))
.add_systems(OnEnter(GameState::Loading), load_level);
}
}
The common conception about saving and loading game state in Bevy (or ECS in general) is that you just have to do serialize all the components of all the entities into a file, and when loading just rebuild the scene from that component data. That, however, is very similar to the common conception about editors for Bevy - the very conception that Yoleck is trying to be an alternative to. So I figure Yoleck can offer a similar alternative to. Since Yoleck already has a concise representation of the level, one that does not have to store every single component because it can build them from YoleckEntityType
s and YoleckComponent
s, it shouldn't be that much more work to save just the changed data.
Advantages over entire-scene save:
Yoleck itself is not going to handle the storage medium for the saved files. Doing that will lock users to a specific persistence solution. Instead, when initiating a save Yoleck will send an event with a serde_json::Value
that represents the state, and when loading the user will have to provide that serde_json::Value
. Note that serde_json::Value
is not an actual JSON - it's just an in-memory data type with JSON semantics. It can be stored as a more compact and less human-readable format.
Most straightforward way - make it a resource.
The idea is to allow levels to refer other levels. For example - a door entity can have a field with the name of another level that door leads to.
This is EXACTLY what i have been looking for - for so long! Im so excited to see this. Im interested to see forks and additions that add things like click-and-drag wall creation, ground textures, and more. Very exciting.
Please add Bevy::reflect support for exposed field that can be managed from client side.
Maybe feature flag it
Currently Yoleck levels are loaded with a resource - YoleckLoadingCommand
. It would be better if instead a level would be entities, and we could load/unload the level by placing components on them (or maybe just sending events?). The benefits would be:
e.g. graphs of which levels lead to which other levels.
As the title says. It could be some problem with the way my project is structured (maybe a conflicting crate?) but I just wanted to put it out there in case anyone is having the same issue. For me, explicitly adding 'bevy_egui = "0.26.0"' to the Cargo.toml fixes the issue.
Some entities need to refer to other entities. For example - a key that opens a specific door. We need a way to represent this and to provide UI for editing this.
In order to save entities (#23) they need to be Yoleck entities, which currently means they have to be created from the .yol
file. But what if we want to save entities created at runtime? For example - in a strategy game the player will build structures, and we want to save these structures even though they are not in the .yol
file.
To support this, we need to be able to generate Yoleck entities during gameplay.
Instead of editing the level order in egui and saving it in a level index, I want to make the level index a regular level file where each entity is another level, and the components define the order. The editing will be done by dragging and dropping.
This will also allow non-linear level structure, where the levels map can be a graph instead of a list.
Example usecase - #21. When an entity refers to another entity, we want the first entity to click the second entity, we want to be able to do this by editing the target entity, doing something to indicate that we want to pick an entity to refer to, and then click on the other entity.
For this, we need to be able to send an edit system to an "exclusive input mode". In that mode:
In my game project, I have fences that can be a custom length, and you often want pairs of them. Therefore, I added an yoleck editor button that spawns a clone of that object. This required me to fork yoleck and make a couple fields public, and they reasonably should not be public, since there seems to be some invariants to uphold. My suggestion then is to add functionality to allow the editor to spawn editor objects with an arbitrary payload, since this can be useful for all kinds of use cases.
I'll show you how I did this in my project, with some extra context code. You're free to integrate any of the code I paste in this issue into yoleck, but ask if there's something from the rest of my repo (https://github.com/ZeeQyu/sylt) you want to use.
This is the only change I had to do in yoleck (and I'm hoping this won't be required if you move the function inside since it'd be bad if a user starts editing this without maintaining the invariants).
diff --git a/src/lib.rs b/src/lib.rs
index ea0a503..92d7d96 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -315,8 +315,8 @@ impl YoleckTypeHandlers {
/// Fields of the Yoleck editor.
#[derive(Resource)]
pub struct YoleckState {
- entity_being_edited: Option<Entity>,
- level_needs_saving: bool,
+ pub entity_being_edited: Option<Entity>,
+ pub level_needs_saving: bool,
}
impl YoleckState {
And in my code:
pub fn create_editor_object(commands: &mut Commands, writer: &mut EventWriter<YoleckEditorEvent>, yoleck: &mut ResMut<YoleckState>, type_name: &str, value: serde_json::Value) {
let cmd = commands.spawn(YoleckRawEntry {
header: YoleckEntryHeader {
type_name: String::from(type_name),
name: String::from(""),
},
data: value,
});
writer.send(YoleckEditorEvent::EntitySelected(cmd.id()));
yoleck.entity_being_edited = Some(cmd.id());
yoleck.level_needs_saving = true;
}
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct EditorFence {
#[serde(default)]
position: Vec2,
#[serde(default)]
orientation: FenceOrientation,
#[serde(default)]
section_length: f32,
}
#[derive(Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum FenceOrientation {
#[default]
Horizontal,
Vertical,
}
fn edit_fence(
mut edit: YoleckEdit<EditorFence>,
configuration: Res<Configuration>,
mut commands: Commands,
mut writer: EventWriter<YoleckEditorEvent>,
mut yoleck: ResMut<YoleckState>,
) {
edit.edit(|ctx, data, ui| {
if ui.add(egui::Button::new("Spawn copy")).clicked() {
let offset_axis = match data.orientation {
FenceOrientation::Horizontal => -Vec2::Y,
FenceOrientation::Vertical => Vec2::X,
};
let value = serde_json::to_value(EditorFence { position: data.position + offset_axis * 20.0, ..data.clone() }).unwrap();
create_editor_object(&mut commands, &mut writer, &mut yoleck, FENCE_NAME, value);
}
// --snip--
});
}
I have no clue how to solve this elegantly, without requiring users to add EventWriter and ResMut to their systems, so I'm hoping you find a better way!
Currently for both 2D and 3D I'm doing AABB tests to check which cursor the entity points at. For 3D, it would be more accurate to test against the triangles in the mesh.
e.g. - handles the editor can interact with
Support:
The current bevy_mod_picking and bevy_transform_gizmo integration feels clunky and cumbersome.
There is a bug with Bevy's assets loader (bevyengine/bevy#9337) That gets triggered when Yoleck:
In all these cases, Yoleck replaces all the entities under its control and creates new ones, but the new ones will only start loading their assets on the next frame, when the Yoleck components are registered and the populate systems run.
This will probably get fixed in Bevy 0.12 with Bevy Asset V2, but until then I'd like Yoleck to have a workaround.
Allow Yoleck managed entities refer other Yoleck managed entities.
Migration guide states that
YoleckUi is now a non-Send resource, which means it can no longer be accessed as a regular Res/ResMut. It must now be accessed as NonSend/NonSendMut.
Hopefully once emilk/egui#3148 is fixed (and gets in to bevy_egui) this can be changed back.
This has been fixed. I tried to update to bevy_egui 0.22 and move yoleckui to res/resmut. However, I got stuck in the fact that egui::Ui doesn't implement Default or bevy::ecs::FromWorld, can you give me any light to help with this change?
I tried to switch to egui::context, but it did not expose the Ui as a consumable.
Hope you are safe
Registering entity types via code can be tedious. Users would prefer to use some UI to define them - something like bevy_editor_pls, or an external editor (like Blender) that can add metadata to the scene which Bevy can translate into components.
Yoleck needs to be able to read such files and convert them to YoleckEntityType
s at runtime. That way the user can create entity types with the UI and then place them in the level with Yoleck. Ideally, without even restarting the editor instance.
Yoleck itself should not add that external UI as a dependency. Instead, it should provide the mechanism for importing files (of unknown type!) and then some eternal crate - let's call it for now "bevy-yoleck-pls" - will add the support for specific file types. bevy-yoleck-pls should also allow switching between Yoleck and bevy_editor_pls while editing.
Note that components will still need to be defined (and registered!) by code. Can't escape that.
Also note that Yoleck will need metadata to determine two things:
YoleckComponent
should the entity type have? These are treated different from regular components. Luckily, it's easy enough to set a default (usually it'd just be Vpeol2dPosition
/Vpeol3dPosition
) and Yoleck itself could provide the UI for editing the YoleckComponent
s.Since all that metadata has good defaults, it should not be an issue to have Yoleck list a directory and automatically import all the entity definition files from there, and just have some UI to configure them afterwards.
When you try to drag them it sends the coordinates of the ofsetted entity, not the parent one.
Would also be nice to mark hovered entities, to make it easier to tell what will be selected.
Currently, when adding a new entity in the editor, all the YoleckComponent
s are initialized to their default value. For the position component, this usually means the origin. This can get a bit cumbersome:
It could be nice if instead of just spawning the thing at the origin, the user would click in the world space where they want to place the new entity.
Note that because there is no position in Yoleck's core (it's part of Vpeol), this will have to be user-customizable. On the plus size this could mean that the same mechanism can be used for other initializations.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.