Skip to content

Commit

Permalink
Merge pull request #63 from bytestring-net/dev
Browse files Browse the repository at this point in the history
Docs update
  • Loading branch information
IDEDARY authored Jul 30, 2024
2 parents 366e36e + 6f9fbdc commit 713932e
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 22 deletions.
20 changes: 16 additions & 4 deletions docs/src/advanced/interactivity.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,38 @@

Lunex implements its interactivity on top of `Bevy_mod_picking`. If you need more control over interactions, consider researching this crate.

Interactivity is achieved by utilizing Events and Systems. Lunex provides several components to simplify the process. First, ensure your entity is pickable by adding `PickableBundle` for entities with sprites or meshes, and `UiZoneBundle` for entities without.
This crate allows us to detect mouse clicks, hovers, drags, etc.

To block picking, set `Pickable::IGNORE` on non-UI entities.
### Requirements

Please note that `Cursor2d` **MUST** be spawned for any picking to work.

### Getting started

Interactivity is achieved by utilizing Events and Systems. Lunex provides several components to simplify the process. First, ensure your entity is pickable by adding `PickableBundle` for entities with sprites or meshes, and `UiZoneBundle` for entities without sprite or meshes.

Pay attention to **DEPTH** of entities (Not just UI entities), because if they overlap your pickable node, they will block all picking events. To avoid that, you can add `Pickable::IGNORE` component to any entity that might overlap your node.
```rust
// Make it non-obsructable for hit checking (mouse detection)
Pickable::IGNORE,
```

To check for mouse clicks, listen to `UiClickEvent` in your systems using the following code:
```rust
// Here we can listen to UiClick events that hold entity ID, then retrieve that entity from our query
fn button_click_system(mut events: EventReader<UiClickEvent>, query: Query<&CustomButton>) {
// Iterate over all events
for event in events.read() {
// Get our entity
if let Ok(button) = query.get(event.target) {
// Process our button click
info!("Pressed button: {}", button.text);
}
}
}
```

Note that `UiClickEvent` is not emitted automatically; you need to add the component to emit this event when `Pointer<Down>` is triggered if you decide to make your own components.
Note that `UiClickEvent` is **NOT** emitted automatically; you need to add a component to emit this event when `Pointer<Down>` is triggered, if you decide to make your own components.
```rust
// If we click on this node, it will emmit UiClick event on itself
UiClickEmitter::SELF,
Expand All @@ -33,7 +45,7 @@ UiClickEmitter::new(entity),
This component is really useful when creating complex components. You want `UiClickEvent` to be emmited from the top entity in a component, so users can listen to them. This component allows you to do exactly that.


Another components that might prove useful are:
Another components that you might find useful are:
```rust
// If it detects UiClick event for this entity it will despawn the specified entity, great for despawning routes
OnUiClickDespawn::new(entity),
Expand Down
2 changes: 1 addition & 1 deletion docs/src/advanced/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ UiText2dBundle {
},
```

You can also decouple the font size from the logical font size by adding this component. This new value will be used instead and native bevy font size will used purely for rendering (font resolution).
You can also decouple the font size from the logical font size by adding this component. This new value will be used instead and native bevy font size will be used purely for rendering (font resolution).

```rust
UiTextSize::new().size(Rh(5.0)),
Expand Down
2 changes: 2 additions & 0 deletions docs/src/advanced/worldspace_ui.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Worldspace UI

For now you can look at [worldspace example](https://github.com/bytestring-net/bevy_lunex/tree/main/examples/worldspace) on GitHub.

*Coming soon...*
9 changes: 3 additions & 6 deletions docs/src/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Centered around **Bevy ECS**, Bevy_Lunex allows developers to manage their UI el
- Requiring worldspace (diegetic) UI

- **Lunex is not optimal for**:
- Making a dektop application (WIP)
- Wanting to make UI quickly (WIP)
- Making a dektop application
- Wanting to make UI quickly
- Complex editor-like UI


### Syntax Example
Expand Down Expand Up @@ -79,7 +80,3 @@ commands.spawn((

- **Resizable Layouts:**
As a game-first UI framework, Bevy_Lunex is designed to support all aspect ratios and resolutions out of the box. UI layouts automatically adapt to different window sizes without collapsing or requiring explicit instructions for various circumstances. For instance, a UI designed for a 1920x1080 pixel window will maintain its layout proportionally when scaled down to a 1280x720 pixel window, simply appearing smaller. This behavior is ideal for games, though it may differ from traditional HTML-based layouts. For regular applications requiring different behavior, the `Div` layout (currently a work in progress) is recommended.

### Comparison with `bevy_ui`

While `bevy_ui` offers a straightforward approach to UI creation within Bevy, Bevy_Lunex provides a more advanced and hands-on alternative. Additionally, the ability to integrate both 2D and 3D elements and the seamless extension of UI behavior through ECS components make Bevy_Lunex a powerful tool for developers aiming to create sophisticated and stylized user interfaces.
32 changes: 22 additions & 10 deletions docs/src/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

## Explanation

Lunex works by first creating an entity that will contain the future UI. This entity is called `UiTree` and has a component with the same name. Afterwards, any entity that will be part of that UI needs to be spawned as it's child.
Lunex is first and foremost a worldspace layout engine, which means that **ALL** your UI entities exist in the same space as your game objects.

This for example means, that if you have a moving camera, you **HAVE TO** spawn your UI as children of the camera, otherwise your UI would stay where you spawned it. _(Most likely at [0,0,0])_

## Boilerplate

First import Lunex library

```rust
use bevy_lunex::prelude::*;
```

Then we need to add `UiPlugin` to our app.
Then we add `UiPlugin` to our app.

```rust
fn main() {
Expand All @@ -24,10 +28,11 @@ Thats it for the boilerplate!

## Start

Because the library supports a lot of different use cases, we need to specify what dimensions will the UI be rendered with.
Lunex works by creating an entity that will contain all UI within it. This entity is called `UiTree` and has a component with the same name. Afterwards, any entity that will be part of that UI needs to be spawned as it's **direct** child.

Because the library supports a lot of different use cases, we **MUST** specify what source dimensions will the UI be be using. In most cases, we want it to take camera's viewport size.

Right now we want to use the window size, so we will use the default marker component and add it to our camera.
This will make the camera pipe it's size into our future `UiTree` which also needs to have the same marker applied.
To do that, you can tag your camera with the default `MainUi` marker provided by Lunex. If the library detects a camera with this marker, **ALL** `UiTree`s with the same tag will use this camera's size as size source.

```rust
commands.spawn((
Expand All @@ -39,9 +44,9 @@ commands.spawn((
));
```

Now we need create our `UiTree` entity. The core components are `UiTree` + `Dimension` + `Transform`. The `UiTreeBundle` already contains these components for our ease of use.
### UiTree

The newly introduced `Dimension` component is used as the source size for the UI system. We also need to add the `MovableByCamera` component so our entity will receive updates from camera. The last step is adding the default `MainUi` marker as a generic.
Now we need create our `UiTree` entity. Use the bundle below and attach `MovableByCamera` component so our `UiTree` will receive updates from our camera. The last step is adding the default `MainUi` marker as a generic.

```rust
commands.spawn((
Expand All @@ -52,13 +57,18 @@ commands.spawn((
UiTreeBundle::<MainUi>::from(UiTree::new2d("Hello UI!")),

)).with_children(|ui| {
// Here we will spawn our UI as children
// Here we will spawn our UI as direct children
});
```

Now, any entity with `UiLayout` + `UiLink` spawned as a child of the `UiTree` will be managed as a UI entity. If it has a `Transform` component, it will get aligned based on the `UiLayout` calculations taking place in the parent `UiTree`. If it has a `Dimension` component then its size will also get updated by the `UiTree` output. This allows you to create your own systems reacting to changes in `Dimension` and `Transform` components.
### UiNodes

You can add a `UiImage2dBundle` to the entity to add images to your widgets. Or you can add another `UiTree` as a child, which will use the computed size output in `Dimension` component instead of a `Camera` piping the size to it.
Now, any entity with `UiLayout` + `UiLink` spawned as a child of the `UiTree` will be managed as a UI entity. If it has a `Transform` component, it will get aligned based on the `UiLayout` calculations taking place in the parent `UiTree`. If it has a `Dimension` component then its size will also get updated by the `UiTree` output.

This allows you to create your own systems reacting to changes in `Dimension` and `Transform` components.
You can very easily for example write a custom renderer system, that styles your nodes based on measurements in these components.

To quickly attach an image to our node, you can add a `UiImage2dBundle` to the entity to add images to your widgets.

The generic in `pack::<S>()` represents state. For now leave it at `Base`, but when you for example later want to add hover animation use `Hover` instead.

Expand Down Expand Up @@ -93,3 +103,5 @@ As you can see in the terminal (If you have enabled `debug` feature or added the
|-> Root == Window [pos: (x: 20, y: 20) size: (x: -40 + 100%, y: -40 + 100%)]
| |-> Rectangle == Solid [size: (x: 1920, y: 1080) align_x: 0 align_y: 0]
```

You can read more about this hierarchy in [**Linking**](advanced/linking.md)
2 changes: 1 addition & 1 deletion examples/worldspace/src/boilerplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub fn default_plugins() -> PluginGroupBuilder {
DefaultPlugins.set (
WindowPlugin {
primary_window: Some(Window {
title: "Bevycom".into(),
title: "Worldspace".into(),
mode: bevy::window::WindowMode::Windowed,
present_mode: bevy::window::PresentMode::AutoNoVsync,
resolution: bevy::window::WindowResolution::new(1280.0, 720.0),
Expand Down

0 comments on commit 713932e

Please sign in to comment.