Closed Hellzbellz123 closed 1 year ago
one other "gripe" i have is that before the rewrite all kayak components where contained under a single entity, this made it much more difficult too despawn the ui or control visiblity. i havent noticed how to control visiblity with the new examples or in the book, though i may have missed/not understood it
one other "gripe" i have is that before the rewrite all kayak components where contained under a single entity, this made it much more difficult too despawn the ui or control visiblity. i havent noticed how to control visiblity with the new examples or in the book, though i may have missed/not understood it
I think visibility will be handled with some sort of visibility enum on the styles struct. Likely with three values: Display
, Hidden
, None
. Hidden would allow the widget to still affect the layout.
that sounds fantastic, currently im managing visibility with a global State
the nice thing about the whole ui being under a single entity was being able to use despawn recursive and keeping entity lists human readable, note below my EnemyContainer and MapContainer entitys, i dont know if thats doable with the new rewrite but if not a better way to despawn entitys in the context,
my current solution is to use the below system, but this makes it strange to say despawn the menu ui widgets and and only have the ingame ui entities spawned. if this is not a good approach i would love to hear others.
pub fn despawn_ui(
mut commands: Commands,
to_despawn: Query<Entity, With<CameraUIKayak>>,
widts: Query<Entity, With<KStyle>>,
) {
for entity in to_despawn.iter() {
info!("despawning kayak_root_context: {:#?}", entity);
commands.entity(entity).despawn_recursive();
for widget in widts.iter() {
commands.entity(widget).despawn_recursive();
}
}
}
Okay so I recently struggled with this in my own game. I found a few issues around diffing. Diffing widgets is hard when they have almost no changes! I made a few poor assumptions around that. I'm hoping I can fix those by implementing a better diffing strategy but I need to think about it more.
Here is a working example widget(from my game), you'll need to use the bugfixes
branch and use the new context system. It's a bit broken of a branch at the moment(none of the examples work), but I should be wrapping it up and merging it soon.
#[derive(Component, Default, Clone, PartialEq, Eq)]
pub struct GameApp;
impl Widget for GameApp {}
#[derive(Bundle)]
pub struct GameAppBundle {
pub app: GameApp,
pub styles: KStyle,
pub computed_styles: ComputedStyles,
pub widget_name: WidgetName,
}
impl Default for GameAppBundle {
fn default() -> Self {
Self {
app: Default::default(),
styles: Default::default(),
computed_styles: ComputedStyles::default(),
widget_name: GameApp::default().get_name(),
}
}
}
pub fn update(
In((widget_context, entity, previous_props_entity)): In<(KayakWidgetContext, Entity, Entity)>,
mut prev_state: Local<GameState>,
widget_param: WidgetParam<KayakApp, EmptyState>,
game_state: Res<CurrentState<GameState>>,
mut commands: Commands,
) -> bool {
// Diffing fails here because to the context it looks like nothing changed until the children start to render.
// Diffing needs to start looking a bit deeper to determine if the children of a widget have changed.
// And to figure out if a widget has gone from A to B.
// As a work around we can tell the tree that we actually have an entirely new tree.
if game_state.is_changed() && game_state.0 != *prev_state {
*prev_state = game_state.0;
for child in widget_context.get_children(entity).iter() {
// Despawns entity data without losing track of entity id's
// and removes the widgets from the tree.
// depsawn_safe can be found in the `bugfixes` branch, but the name will likely change in the future.
// As it's not actually unsafe rust code...
widget_context.despawn_safe(&mut commands, *child);
}
}
widget_param.has_changed(&widget_context, entity, previous_props_entity)
|| game_state.is_changed()
}
pub fn render(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands,
mut query: Query<(&KStyle, &mut ComputedStyles)>,
game_state: Res<CurrentState<GameState>>,
) -> bool {
if let Ok((app_style, mut computed_styles)) = query.get_mut(entity) {
*computed_styles = KStyle::default()
.with_style(KStyle {
render_command: RenderCommand::Layout.into(),
..Default::default()
})
.with_style(app_style)
.into();
let parent_id = Some(entity);
rsx! {
<ElementBundle>
{
match game_state.0 {
GameState::Generation | GameState::GenerateEditor => {
constructor! {
<GenerationWidgetBundle />
}
},
GameState::Playing => {
constructor! {
<ElementBundle>
<TopBarBundle />
<FpsWidgetBundle
styles={KStyle {
position_type: KPositionType::SelfDirected.into(),
top: Units::Pixels(5.0).into(),
right: Units::Pixels(5.0).into(),
..Default::default()
}}
/>
</ElementBundle>
}
},
}
}
</ElementBundle>
};
}
true
}
And how to spawn a context now:
// Note: None of this is required except for the `CameraUiKayak` part.
let camera_entity = commands.spawn(Camera2dBundle {
projection: OrthographicProjection {
far: 1000.0,
scale: 0.5,
..Default::default()
},
camera: Camera {
hdr: true,
..Camera::default()
},
tonemapping: Tonemapping::Enabled { deband_dither: true },
..Camera2dBundle::default()
}).insert(CameraUIKayak::default()).id();
let mut widget_context = KayakRootContext::new(camera_entity);
widget_context.add_plugin(KayakWidgetsContextPlugin);
widget_context.add_plugin(ui::GameUIPluign);
let parent_id = None;
rsx! {
<KayakAppBundle>
<ui::app::GameAppBundle />
</KayakAppBundle>
};
commands.spawn((widget_context, EventDispatcher::default()));
when using the git version of kayak_ui, cargo doesnt seem to want to play nice with pulling kayak_font from github if i have it in my cargo.toml, however bevy assetloader needs the type in order to function correctly.
re-exporting the KayakFont would be helpful for anyone with this use case but i dont think anyone else would use it otherwise.