Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sprite Highlighting #315

Open
StrikeForceZero opened this issue Feb 20, 2024 · 1 comment
Open

Sprite Highlighting #315

StrikeForceZero opened this issue Feb 20, 2024 · 1 comment

Comments

@StrikeForceZero
Copy link
Contributor

StrikeForceZero commented Feb 20, 2024

After some experimentation, I discovered that this mod does not support highlighting sprites; it just picks them.

Or I'm failing to configure the mod correctly, but after reviewing the code; none of the systems seem to trigger because they all rely on Handle<ColorMaterial> when Sprites use Handle<Image>. However, the sprites color overlay is controlled by the Sprite.color field.

Should we add support for highlighting sprites?

I'd be happy to submit a PR, but this POC might be considered naive, and there's probably a better way to incorporate this.

I tried my best to maintain the expectation that it would be using ColorMaterial so the existing API to use or override the GlobalHighlight or individual Highlight is still possible.

Below is my POC if interested:

POC

can be dropped into any project as a plugin that also uses the PickingPlugin with highlighting enabled

use bevy::prelude::*;
#[cfg(feature = "egui")]
use bevy_inspector_egui::prelude::*;
use bevy_mod_picking::focus::PickingInteraction;
use bevy_mod_picking::highlight::{GlobalHighlight, Highlight, InitialHighlight, PickHighlight};
use bevy_mod_picking::picking_core::PickSet;
use bevy_mod_picking::prelude::PickSelection;


/// Automatically records the "initial" state of highlightable entities.
/// monkey patch for Sprite
fn get_sprite_initial_highlight_asset(
    mut commands: Commands,
    mut colors: ResMut<Assets<ColorMaterial>>,
    entity_asset_query: Query<(Entity, &Sprite), Added<PickHighlight>>,
    mut highlighting_query: Query<Option<&mut InitialHighlight<ColorMaterial>>>,
) {
    for (entity, sprite) in entity_asset_query.iter() {
        let color_handle = colors.add(ColorMaterial::from(sprite.color));
        commands.entity(entity).insert(color_handle.clone());
        match highlighting_query.get_mut(entity) {
            Ok(Some(mut highlighting)) => highlighting.initial = color_handle,
            _ => {
                commands.entity(entity).insert(InitialHighlight {
                    initial: color_handle,
                });
            }
        }
    }
}

/// Apply highlighting assets to entities based on their state.
/// monkey patch for Sprite
fn update_sprite_highlight(
    global_defaults: Res<GlobalHighlight<ColorMaterial>>,
    color_materials: Res<Assets<ColorMaterial>>,
    mut query: Query<
        (
            &mut Sprite,
            &PickingInteraction,
            Option<&InitialHighlight<ColorMaterial>>,
            Option<&Highlight<ColorMaterial>>,
        ),
        Changed<PickingInteraction>>,
) {
    for (mut sprite, picking_interaction, initial_highlight, highlight_override) in query.iter_mut() {
        let material_handle = match picking_interaction {
            PickingInteraction::Pressed => Some(global_defaults.pressed(&highlight_override)),
            PickingInteraction::Hovered => Some(global_defaults.hovered(&highlight_override)),
            PickingInteraction::None => initial_highlight.map_or(None, |h| Some(h.initial.to_owned())),
        };
        if let Some(material_handle) = material_handle {
            if let Some(color_material) = color_materials.get(material_handle) {
                sprite.color = color_material.color;
            }
        }
    }
}

/// If the interaction state of a selected entity is `None`, set the highlight color to `selected`.
/// monkey patch for Sprite
fn update_sprite_selection(
    global_defaults: Res<GlobalHighlight<ColorMaterial>>,
    color_materials: Res<Assets<ColorMaterial>>,
    mut interaction_query: Query<
        (
            &mut Sprite,
            &PickingInteraction,
            &PickSelection,
            &InitialHighlight<ColorMaterial>,
            Option<&Highlight<ColorMaterial>>,
        ),
        Or<(Changed<PickSelection>, Changed<PickingInteraction>)>,
    >,
) {
    for (mut sprite, interaction, selection, init_highlight, h_override) in &mut interaction_query {
        if let PickingInteraction::None = interaction {
            let material= if selection.is_selected {
                global_defaults.selected(&h_override)
            } else {
                init_highlight.initial.to_owned()
            };
            if let Some(color_material) = color_materials.get(material) {
                sprite.color = color_material.color;
            }
        }
    }
}

pub struct SubPlugin;

impl Plugin for SubPlugin {
    fn build(&self, app: &mut App) {
        app
            .add_systems(PreUpdate, (
                    get_sprite_initial_highlight_asset,
                    update_sprite_highlight,
                    update_sprite_selection,
                )
                .chain()
                .in_set(PickSet::Last)
            )
        ;
    }
}
@aevyrie
Copy link
Owner

aevyrie commented Feb 23, 2024

The highlighting plugin was built to support any asset type. You can add a HighlightingPlugin<Image>, which will allow you to swap out an entity's Image asset based on picking state. If we want to support changing properties other than assets, we should probably make a more generic highlighting plugin that can query any component on an entity. However, at this point, it sounds like it would be much simpler to implement this with an event listener.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants