katharostech / bevy_ldtk

A Bevy plugin for loading the LDtk 2D tile map format.
Other
23 stars 6 forks source link

Use map data in systems #6

Closed MTRNord closed 3 years ago

MTRNord commented 3 years ago

Hi :) How do I access the map from a system? I am new to bevy so I might have missed something. My first try was to use map: Query<LdtkMap> but that didnt work.

Basicly I want to get from the map the entities to draw them as needed. And the required information is stored in the map.

zicklag commented 3 years ago

Hi there!

You were very close with Query<&LdtkMap>, you actually have to use Query<&Handle<LdtkMap>>. I'll have to add this to the docs apparently. 😃 Thanks for asking. :+1:

pub fn setup_collisions(
    map_query: Query<&Handle<LdtkMap>>,
    map_assets: Res<Assets<LdtkMap>>,
) {
    // Loop through all of the maps
    for map_handle in map_query.iter() {
        // We have to `if let` here because asset server may not have finished loading
        // the map yet.
        if let Some(map) = map_assets.get(map_handle) {
            // Do something with map data
        }
    }
}

At that point you have full access to the whole map data structure which you can do whatever you want with.

MTRNord commented 3 years ago

awesome! yeah having that in the example would have helped :)

zicklag commented 3 years ago

You can leave the issue open and I'll close it when I get a good example in. :smile: :+1:

MTRNord commented 3 years ago

@zicklag if you have the time and want to: An example to get the coordinates of the entities using this would also be nice. I did get the texture loaded as wanted but I am kinda stuck wrapping my head around how to find the x,y coordinates for the entity :) I am probably just missing something again. (aka do i need Layer defs or layer instances, or both and how to bring them into the bevy coordinate format)

zicklag commented 3 years ago

There may be minor typos in the below example, but heres a modification of the actual code I've used in my game for spawning the player:

if let Some(map) = maps.get(handle) {
    // This is the default level, but if you spawned a different level, put that ID here
    let level_idx = 0;

    // Get the level from the project
    let level = &map.project.levels[level_idx];

    /// Find the entities layer
    let entities_layer = level
        .layer_instances
        .as_ref() // get a reference to the layer instances
        .unwrap() // Unwrap the option ( this could be None, if there are no layers )
        .iter() // Iterate over the layers
        .filter(|&x| x.__identifier == "MyEntities") // Filter on the one we want
        .next() // Get it
        .unwrap() // Unwrap it ( would be None if it could not find a layer "MyEntities" );

    // Get the specific entity you want
    let player_start = entities_layer
        .entity_instances
        .iter() // Iterate over our entities in the layer
        .filter(|x| x.__identifier == "PlayerStart") // Find the one we want
        .next() // Get it
        .unwrap() // Unwrap it;

    // Get the number of layers in the map and add one to it: this is how high we need to
    // spawn the player so that he is on top of all the maps
    let player_z = level.layer_instances.as_ref().unwrap().len() as f32 + 1.0;

    // Spawn the entity!
    commands
        .spawn(SpriteBundle {
            // Set your sprite stuff
            transform: Transform::from_xyz(
                // The player x position is the entity's x position from the map data
                player_start.px[0],
                // The player y position is the entity's y position from the map data, but
                // multiplied by negative one because in the LDtk map +y means down and not up.
                player_start.px[1] * -1,
                // Spawn the player with the z value we determined earlier
                player_z
            ),
            ..Default::default()
        });
    }
}

It's a little wordier than I would like: the LDtk map format could probably use some helpers to make this kind of stuff a little easier, but at least for now all the data is there. If me or my team get the chance sometime we might try to add some helpers.

MTRNord commented 3 years ago

Hm I guess it should work but for some reason it seems like it ends up not where it is in the editor.

Editor:

grafik

Game:

grafik

(And yes due to laziness and learning purposes I am using one of the starter levels the editor brought)

zicklag commented 3 years ago

Ah, do you have the center_map option set to true in when you spawn the map? That will cause the coordinates to be off. The setting is really more of a convenience for when you just want to display the map.

I guess that's a little confusing maybe. :thinking: maybe I shouldn't have the option to avoid confusion? But then maybe people would be annoyed that they couldn't spawn the map without it. I'm open to suggestions on that point. :slightly_smiling_face:


(And yes due to laziness and learning purposes I am using one of the starter levels the editor brought)

That ain't lazyness that's efficiency! 😉

MTRNord commented 3 years ago

Ah yeah :) though I think the coordinate_at_mouse example thingy by the engine has a conversion for calculating the coordinates based on the window size. That should move it in the right place right?

zicklag commented 3 years ago

Hmm I haven't seen that before. I don't know. If you want it centered or something during gameplay you would most-likely just move the camera to follow the player or something like that.

MTRNord commented 3 years ago

Intresting enough without the center_map set to true it is different wrong.

grafik

I guess it might be due to using a 4k monitor :)

zicklag commented 3 years ago

Hmm, that's interesting. Is the map scale set to 1? I think the monitor scale shouldn't make a difference.

MTRNord commented 3 years ago

Ahhh thats it. It was set to 4. Am I right to assume multiplying each coordinate with the scale factor will fix the issue? (It works with scale 1 but scale 1 doesnt cover the screen the way I want it too)

zicklag commented 3 years ago

Yes, you could multiply everything by 4 and I think it would work, but there's a much more awesome way to do it that will work no matter what your screen size. You just have to spawn the camera with different settings that tell it to fix the height of the camera and then automatically reveal the rest ofthe scene along the width of the camera, and to also scale the whole scene up if the height of the screen is bigger than the height of the screen:

commands
                .spawn(OrthographicCameraBundle {
                    orthographic_projection: OrthographicProjection {
                        far: 1024.0, // This gives us 1024 layers,
                        scale: 200, // How many pixels high in the game
                        scaling_mode: ScalingMode::FixedVertical, 
                        ..Default::default()
                    },
                    ..OrthographicCameraBundle::new_2d()
                })
MTRNord commented 3 years ago

Oh yeah thats much cooler :D Thanks for all the help!

MTRNord commented 3 years ago

Ah but that doesnt work in 0-4 apparently due to missing scale and scaling_mode fields :D I guess I am now movin to the latest version now

zicklag commented 3 years ago

Oh, yeah, I forgot about that. 0.5 should be comming out soon-ish I think so looking forward to that. :)

Oh yeah thats much cooler :D Thanks for all the help!

Glad I could help 😃

MTRNord commented 3 years ago

Just had to figure out dep hell :D

Side note: This breaks with 0.5 after bevy commit 079b3ade89195ef1aa4bcf1f8c0740a6572b84b6 (thats the las working one or me)

zicklag commented 3 years ago

Ah, gotcha. Good job finding the working one! After 0.5 is released I'll make a new bevy_ldtk release that works.

MTRNord commented 3 years ago

Yeah thats fine :D No rush :3 Learned today more in this issue than in the whole university semester ;P

zicklag commented 3 years ago

Fixed in 173ecbd14157423e45327c451000c8dc6bb0eddf.