Skip to content

Commit

Permalink
Add dungeon example
Browse files Browse the repository at this point in the history
This gives us an example that _actually_ showcases
what this library is capable of providing.
  • Loading branch information
jgayfer committed May 29, 2024
1 parent 8358c91 commit 14a14ec
Show file tree
Hide file tree
Showing 7 changed files with 512 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ authors = ["James Gayfer"]
repository = "https://github.com/jgayfer/bevy_light_2d"
license-file = "LICENSE"
readme = "README.md"
exclude = ["assets/*", "static/*"]

[dependencies]
bevy = { version = "0.13", default-features = false, features = [
"bevy_render",
"bevy_core_pipeline",
"bevy_winit",
"bevy_sprite",
"png",
"x11"
] }

Expand All @@ -34,3 +36,7 @@ path = "examples/basic.rs"
[[example]]
name = "multiple"
path = "examples/multiple.rs"

[[example]]
name = "dungeon"
path = "examples/dungeon.rs"
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
A general purpose 2d lighting plugin for [`bevy`](https://bevyengine.org/).
Designed to be simple to use, yet expressive enough to fit a variety of needs.

<img src="https://github.com/jgayfer/bevy_light_2d/blob/main/static/dungeon.gif" width="400">

## Features

- Component driven design
Expand Down Expand Up @@ -79,3 +81,7 @@ I'd like to thank the authors of the below crates; they were a significant sourc
- [`bevy-magic-light-2d`](https://github.com/zaycev/bevy-magic-light-2d)
- [`bevy_2d_screen_space_lightmaps`](https://github.com/goto64/bevy_2d_screen_space_lightmaps)
- [`bevy_incandescent`](https://github.com/443eb9/bevy_incandescent)

## Asset credits

- [Pixel Dungeon](https://pixel-poem.itch.io/dungeon-assetpuck) by [`pixel_poem`](https://pixel-poem.itch.io/)
Binary file added assets/candle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/dungeon_tiles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
250 changes: 250 additions & 0 deletions examples/candle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use bevy::prelude::*;
use bevy_light_2d::prelude::*;

const TILE_INDEX: f32 = 0.0;
const ENTITY_INDEX: f32 = 1.0;

fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(ImagePlugin::default_nearest()),
Light2dPlugin,
))
.init_resource::<DungeonTileset>()
.init_resource::<CandleSpritesheet>()
.add_systems(Startup, (setup_camera, set_clear_color))
.add_systems(Startup, (setup_dungeon_tileset, spawn_tiles).chain())
.add_systems(Startup, (setup_candle_spritesheet, spawn_candles).chain())
.add_systems(Update, animate_candles)
.run()
}

#[derive(Resource, Default)]
struct DungeonTileset {
layout: Handle<TextureAtlasLayout>,
texture: Handle<Image>,
}

#[derive(Resource, Default)]
struct CandleSpritesheet {
layout: Handle<TextureAtlasLayout>,
texture: Handle<Image>,
}

#[derive(Component)]
struct Candle;

#[derive(Component, Deref, DerefMut)]
struct AnimationTimer(Timer);

fn setup_camera(mut commands: Commands) {
let mut camera = Camera2dBundle::default();
camera.projection.scale = 0.25;
commands.spawn((
camera,
AmbientLight2d {
brightness: 0.1,
..default()
},
));
}

fn set_clear_color(mut clear_color: ResMut<ClearColor>) {
clear_color.0 = Color::rgb_u8(37, 19, 26);
}

fn animate_candles(
time: Res<Time>,
mut query: Query<(&mut AnimationTimer, &mut TextureAtlas), With<Candle>>,
) {
for (mut timer, mut atlas) in &mut query {
timer.tick(time.delta());
if timer.just_finished() {
atlas.index = (atlas.index + 1) % 4;
}
}
}

fn spawn_candles(mut commands: Commands, spritesheet: Res<CandleSpritesheet>) {
let light = commands
.spawn((PointLight2dBundle {
transform: Transform::from_xyz(0.0, 4.0, ENTITY_INDEX),
point_light: PointLight2d {
radius: 48.0,
color: Color::YELLOW,
intensity: 25.0,
falloff: 4.0,
},
..default()
},))
.id();

commands
.spawn((
Candle,
AnimationTimer(Timer::from_seconds(0.2, TimerMode::Repeating)),
SpriteSheetBundle {
transform: Transform::from_xyz(0., 2., ENTITY_INDEX),
texture: spritesheet.texture.clone(),
atlas: TextureAtlas {
layout: spritesheet.layout.clone(),
..default()
},
..default()
},
))
.add_child(light);
}

fn spawn_tiles(mut commands: Commands, tileset: Res<DungeonTileset>) {
let mut spawn_wall_tile = |position: (i32, i32), index: usize| {
spawn_from_atlas(
&mut commands,
tile_translation(position.0, position.1).extend(TILE_INDEX),
index,
tileset.layout.clone(),
tileset.texture.clone(),
);
};

// First row
spawn_wall_tile((-3, 2), LEFT_WALL_A);
spawn_wall_tile((-2, 2), TOP_WALL_A);
spawn_wall_tile((-1, 2), TOP_WALL_B);
spawn_wall_tile((0, 2), TOP_WALL_C);
spawn_wall_tile((1, 2), TOP_WALL_A);
spawn_wall_tile((2, 2), TOP_WALL_D);
spawn_wall_tile((3, 2), RIGHT_WALL_A);

// Second row
spawn_wall_tile((-3, 1), LEFT_WALL_B);
spawn_wall_tile((-2, 1), TOP_LEFT_FLOOR);
spawn_wall_tile((-1, 1), TOP_FLOOR_A);
spawn_wall_tile((0, 1), TOP_FLOOR_B);
spawn_wall_tile((1, 1), TOP_FLOOR_A);
spawn_wall_tile((2, 1), TOP_RIGHT_FLOOR);
spawn_wall_tile((3, 1), RIGHT_WALL_B);

// Third row
spawn_wall_tile((-3, 0), LEFT_WALL_C);
spawn_wall_tile((-2, 0), LEFT_FLOOR);
spawn_wall_tile((-1, 0), FLOOR_A);
spawn_wall_tile((0, 0), FLOOR_B);
spawn_wall_tile((1, 0), FLOOR_A);
spawn_wall_tile((2, 0), RIGHT_FLOOR);
spawn_wall_tile((3, 0), RIGHT_WALL_C);

// Fourth row
spawn_wall_tile((-3, -1), LEFT_WALL_D);
spawn_wall_tile((-2, -1), BOTTOM_LEFT_FLOOR);
spawn_wall_tile((-1, -1), BOTTOM_FLOOR_A);
spawn_wall_tile((0, -1), BOTTOM_FLOOR_B);
spawn_wall_tile((1, -1), BOTTOM_FLOOR_B);
spawn_wall_tile((2, -1), BOTTOM_RIGHT_FLOOR);
spawn_wall_tile((3, -1), RIGHT_WALL_D);

// Bottom row
spawn_wall_tile((-3, -2), BOTTOM_LEFT_WALL);
spawn_wall_tile((-2, -2), BOTTOM_WALL_A);
spawn_wall_tile((-1, -2), BOTTOM_WALL_B);
spawn_wall_tile((0, -2), BOTTOM_WALL_C);
spawn_wall_tile((1, -2), BOTTOM_WALL_A);
spawn_wall_tile((2, -2), BOTTOM_WALL_D);
spawn_wall_tile((3, -2), BOTTOM_RIGHT_WALL);
}

fn tile_translation(x: i32, y: i32) -> Vec2 {
Vec2::new(x as f32 * 16.0, y as f32 * 16.0)
}

fn spawn_from_atlas(
commands: &mut Commands,
translation: Vec3,
sprite_index: usize,
atlas_handle: Handle<TextureAtlasLayout>,
texture: Handle<Image>,
) {
commands.spawn(SpriteSheetBundle {
transform: Transform {
translation,
..default()
},
texture,
atlas: TextureAtlas {
index: sprite_index,
layout: atlas_handle,
},
..default()
});
}

fn setup_dungeon_tileset(
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
mut dungeon_tileset: ResMut<DungeonTileset>,
) {
dungeon_tileset.texture = asset_server.load("dungeon_tiles.png");
dungeon_tileset.layout = texture_atlas_layouts.add(TextureAtlasLayout::from_grid(
Vec2::new(16.0, 16.0),
10,
10,
None,
None,
));
}

fn setup_candle_spritesheet(
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
mut candle_spritesheet: ResMut<CandleSpritesheet>,
) {
candle_spritesheet.texture = asset_server.load("candle.png");
candle_spritesheet.layout = texture_atlas_layouts.add(TextureAtlasLayout::from_grid(
Vec2::new(16.0, 16.0),
4,
1,
None,
None,
));
}

const TOP_WALL_A: usize = 1;
const TOP_WALL_B: usize = 2;
const TOP_WALL_C: usize = 3;
const TOP_WALL_D: usize = 4;

const LEFT_WALL_A: usize = 0;
const LEFT_WALL_B: usize = 10;
const LEFT_WALL_C: usize = 20;
const LEFT_WALL_D: usize = 20;

const RIGHT_WALL_A: usize = 5;
const RIGHT_WALL_B: usize = 15;
const RIGHT_WALL_C: usize = 25;
const RIGHT_WALL_D: usize = 35;

const BOTTOM_LEFT_WALL: usize = 40;
const BOTTOM_RIGHT_WALL: usize = 45;

const BOTTOM_WALL_A: usize = 41;
const BOTTOM_WALL_B: usize = 42;
const BOTTOM_WALL_C: usize = 43;
const BOTTOM_WALL_D: usize = 44;

const TOP_LEFT_FLOOR: usize = 11;
const TOP_RIGHT_FLOOR: usize = 14;

const TOP_FLOOR_A: usize = 12;
const TOP_FLOOR_B: usize = 13;

const LEFT_FLOOR: usize = 21;
const RIGHT_FLOOR: usize = 24;

const BOTTOM_LEFT_FLOOR: usize = 31;
const BOTTOM_RIGHT_FLOOR: usize = 34;

const BOTTOM_FLOOR_A: usize = 32;
const BOTTOM_FLOOR_B: usize = 33;

const FLOOR_A: usize = 22;
const FLOOR_B: usize = 23;
Loading

0 comments on commit 14a14ec

Please sign in to comment.