Reputation: 125
I am trying to set up a bevy project and use pixelart. Right now, it uses a textureatlas to load sprites from a single image.
All is working, but I cannot find out how to use NearestNeighbor in my sprites. My sprites are blurry at the moment. All relevant documentation seems to be pre-bevy: 0.10 as well and seems to be outdated. I also can't seem to find any information about the relevant migrations. Can anyone help me with a way to implement NearestNeighbor in my project?
Any help would be very much appreciated.
This is how I've currently tried it:
use bevy::asset::Handle;
use bevy::prelude::*;
use bevy::render::render_resource::{AddressMode, FilterMode, SamplerDescriptor};
pub const BACKGROUND_COL: Color = Color::rgb(0.05, 0.05, 0.05);
fn main() {
App::new()
//.add_plugins(DefaultPlugins)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Option::from(Window {
title: "Hi".to_string(),
..default()
}),
..default()
}))
.insert_resource(ClearColor(BACKGROUND_COL))
.add_startup_system(spawn_camera)
.add_startup_system(load_spritemap)
.add_startup_system(spawn_player.in_base_set(StartupSet::PostStartup))
.add_system(bevy::window::close_on_esc)
.run();
}
#[derive(Resource)]
struct Spritemap(Handle<TextureAtlas>);
fn spritemap_fix(
mut ev_asset: EventReader<AssetEvent<TextureAtlas>>,
mut assets: ResMut<Assets<TextureAtlas>>,
) {
for ev in ev_asset.iter() {
match ev {
AssetEvent::Created { handle } => {
if let Some(texture) = assets.get_mut(&handle) {
texture.sampler = SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
mag_filter: FilterMode::Nearest,
min_filter: FilterMode::Nearest,
..Default::default()
};
}
}
AssetEvent::Modified { .. } => {}
AssetEvent::Removed { .. } => {}
}
}
}
fn load_spritemap(
mut commands: Commands,
assets: Res<AssetServer>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
let image = assets.load("Spritemap.png");
let atlas = TextureAtlas::from_grid(
image,
Vec2::splat(8.0),
16,
11,
Some(Vec2::splat(2.0)),
Some(Vec2::splat(1.0)),
);
let atlas_handle = texture_atlases.add(atlas);
commands.insert_resource(Spritemap(atlas_handle));
}
fn spawn_camera(mut commands: Commands) {
commands.spawn(Camera2dBundle {
projection: OrthographicProjection {
scale: 1.00,
..default()
},
..default()
});
}
fn spawn_player(mut commands: Commands, sprites: Res<Spritemap>) {
let mut sprite = TextureAtlasSprite::new(0);
sprite.custom_size = Some(Vec2::splat(1.0));
commands.spawn(SpriteSheetBundle {
sprite,
texture_atlas: sprites.0.clone(),
transform: Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
scale: Vec3::splat(100.0),
..default()
},
..default()
});//.insert(Name::new("Player"))
commands.spawn(Player {
name: "Pascal".to_string()
});
}
#[derive(Component)]
pub struct Player {
pub name: String,
}
My cargo.toml:
[package]
name = "bevy_simplerpg"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev.package."*"]
opt-level = 3
[dependencies]
bevy = "0.10.1"
This is the error message i get:
error[E0609]: no field `sampler` on type `&mut bevy::prelude::TextureAtlas`
--> src\main.rs:37:29
|
37 | texture.sampler = SamplerDescriptor {
| ^^^^^^^ unknown field
|
= note: available fields are: `texture`, `size`, `textures`
I tried figuring out what this entailed, and I got close, but I also kept getting errors using this method.
image.sampler_descriptor
Does not seem to have a
mag_filter
property. I don't know where else to access it.
I also don't know if this even works with TextureAtlas.
Upvotes: 2
Views: 1054
Reputation: 120
Update for Bevy 0.14-0.15:
You can call the default_nearest()
function of ImagePlugin to apply this to all loaded assets.
Simply add this to your DefaultPlugins definition:
App::new()
.add_plugins(DefaultPlugins
.set(ImagePlugin::default_nearest() // this one
)
)
as you would set any other plugin property.
Please comment if you need clarification :)
Upvotes: 2
Reputation: 1587
To do this in Bevy 0.14 without changing every texture in the app by default, you can set it per-texture like so:
#[derive(Resource)]
pub struct TextureHandles {
pub my_texture: Handle<Image>,
}
fn load_material(
mut images: ResMut<Assets<Image>>,
texture_handles: Res<TextureHandles>,
) {
if asset_server.load_state(texture_handles.my_texture.id()) != LoadState::Loaded {
return;
}
let image = images.get_mut(&texture_handles.my_texture).unwrap();
image.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::nearest());
}
The complication here is that this can only be done after the image is loaded, which is done in parallel. The 'handle' you get may not be in a loaded state, hence why we verify the loaded state and return after it isn't ready.
Of course, you don't want this system running on every frame so you'll want to use run conditions to ensure that it only runs when needed. I do this using a state enum that is set to Loading
by default, use criteria to ensure the system only runs when when in Loading
state, then after the change is applied set the state to Loaded
.
Upvotes: 1
Reputation: 8221
You need to access the texture
on the TextureAtlas
and then set the sample_descriptor
of that Image
.
Based on your existing code one way this could be done is like so:
fn spritemap_fix(
mut ev_asset: EventReader<AssetEvent<Image>>,
mut assets: ResMut<Assets<Image>>,
) {
for ev in ev_asset.iter() {
match ev {
AssetEvent::Created { handle } => {
if let Some(texture) = assets.get_mut(&handle) {
texture.sampler_descriptor = ImageSampler::nearest()
}
},
_ => {}
}
}
}
and then don't forget to add the system in your main
function:
fn main() {
App::new()
//.add_plugins(DefaultPlugins)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Option::from(Window {
title: "Hi".to_string(),
..default()
}),
..default()
}))
.insert_resource(ClearColor(BACKGROUND_COL))
.add_startup_system(spawn_camera)
.add_startup_system(load_spritemap)
.add_startup_system(spawn_player.in_base_set(StartupSet::PostStartup))
.add_system(spritemap_fix)
.add_system(bevy::window::close_on_esc)
.run();
}
Upvotes: 1