Skip to content

Commit

Permalink
feat: enemies
Browse files Browse the repository at this point in the history
  • Loading branch information
eerii committed Jul 19, 2024
1 parent a5fa979 commit c585045
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 81 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ release = [ # Only in release (build with --release --no-default-features --feat
]
common = ["input", "navigation", "persist", "pixel_perfect", "ui"]

3d_camera = []
input = ["leafwing-input-manager"]
loading = ["ui"]
menu = ["input", "navigation", "ui"]
Expand Down
31 changes: 20 additions & 11 deletions src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use bevy::prelude::*;

use crate::{
data::{init_data, GameOptions, Persistent},
misc::move_to,
player::Player,
GameState,
};

Expand All @@ -24,6 +26,12 @@ impl Plugin for CameraPlugin {
app.add_systems(
OnEnter(GameState::Startup),
init.after(init_data),
)
.add_systems(
Update,
update_camera
.after(move_to)
.run_if(in_state(GameState::Play)),
);
}
}
Expand Down Expand Up @@ -51,7 +59,6 @@ fn init(mut cmd: Commands, options: Res<Persistent<GameOptions>>) {
let clear_color =
ClearColorConfig::Custom(options.base_color.with_luminance(BACKGROUND_LUMINANCE));

#[cfg(not(feature = "3d_camera"))]
let camera_bundle = Camera2dBundle {
camera: Camera {
clear_color,
Expand All @@ -60,15 +67,17 @@ fn init(mut cmd: Commands, options: Res<Persistent<GameOptions>>) {
..default()
};

#[cfg(feature = "3d_camera")]
let camera_bundle = Camera3dBundle {
camera: Camera {
clear_color,
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 10.0),
..default()
};

cmd.spawn((camera_bundle, GameCamera, FinalCamera));
}

fn update_camera(
player: Query<&Transform, (With<Player>, Without<GameCamera>)>,
mut cam: Query<&mut Transform, With<GameCamera>>,
) {
let Ok(player) = player.get_single() else { return };
let Ok(mut trans) = cam.get_single_mut() else { return };

let target_pos = player.translation.truncate();
let pos = trans.translation.truncate().lerp(target_pos, 0.1);
trans.translation = pos.extend(trans.translation.z);
}
95 changes: 95 additions & 0 deletions src/enemy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use bevy::prelude::*;

use crate::{
assets::{SoundAssets, SpriteAssets},
tilemap::tile_to_pos,
GameState, SCALE,
};

// ······
// Plugin
// ······

pub struct EnemyPlugin;

impl Plugin for EnemyPlugin {
fn build(&self, app: &mut App) {
app.add_event::<DamageEvent>()
.add_systems(
OnEnter(GameState::Play),
init.run_if(run_once()),
)
.add_systems(
Update,
on_damage.run_if(in_state(GameState::Play)),
);
}
}

// ··········
// Components
// ··········

#[derive(Component)]
pub struct Enemy {
pub pos: UVec2,
pub health: u32,
}

// ······
// Events
// ······

#[derive(Event)]
pub struct DamageEvent(pub Entity);

// ·······
// Systems
// ·······

fn init(mut cmd: Commands, sprite_assets: Res<SpriteAssets>) {
for _ in 0..3 {
let (x, y) = (
rand::random::<u32>() % 11,
rand::random::<u32>() % 7,
);
let pos = tile_to_pos(x, y);
cmd.spawn((
SpriteBundle {
transform: Transform::from_translation(pos.extend(5.))
.with_scale(Vec3::splat(SCALE)),
texture: sprite_assets.one_bit.clone(),
..default()
},
TextureAtlas {
layout: sprite_assets.one_bit_atlas.clone(),
index: 29 + 7 * 48,
},
Enemy {
pos: UVec2::new(x, y),
health: 2,
},
));
}
}

fn on_damage(
mut cmd: Commands,
mut enemies: Query<&mut Enemy>,
sound_assets: Res<SoundAssets>,
mut damage_reader: EventReader<DamageEvent>,
) {
for DamageEvent(entity) in damage_reader.read() {
if let Ok(mut enemy) = enemies.get_mut(*entity) {
enemy.health -= 1;
if enemy.health == 0 {
cmd.entity(*entity).despawn();
}
}

cmd.spawn(AudioBundle {
source: sound_assets.boing.clone(),
settings: PlaybackSettings::DESPAWN,
});
}
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ pub mod assets;
pub mod audio;
pub mod camera;
pub mod data;
pub mod enemy;
#[cfg(feature = "input")]
pub mod input;
pub mod misc;
pub mod player;
pub mod tilemap;
#[cfg(feature = "ui")]
Expand Down Expand Up @@ -43,6 +45,8 @@ pub enum GameState {
End,
}

// TODO: Create custom schedules inside update

/// Static configuration
/// Allows to pass options to the game plugin such as the title and resolution.
/// Must be added before the plugin
Expand Down Expand Up @@ -161,6 +165,8 @@ impl Plugin for GamePlugin {
audio::AudioPlugin,
camera::CameraPlugin,
data::DataPlugin,
enemy::EnemyPlugin,
misc::MiscPlugin,
player::PlayerPlugin,
tilemap::TilemapPlugin,
));
Expand Down
89 changes: 89 additions & 0 deletions src/misc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::f32::consts::PI;

use bevy::prelude::*;

use crate::{tilemap::TILE_SEP, GameState};

// ······
// Plugin
// ······

pub struct MiscPlugin;

impl Plugin for MiscPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
move_to.run_if(in_state(GameState::Play)),
);
}
}

// ··········
// Components
// ··········

pub enum Direction {
North,
South,
East,
West,
}

#[derive(Component)]
pub struct MoveTo {
start: Vec2,
target: Vec2,
bump_dir: Option<Direction>,
timer: Timer,
}

impl MoveTo {
pub fn new(start: Vec2, target: Vec2, bump_dir: Option<Direction>) -> Self {
Self {
start,
target,
bump_dir,
timer: Timer::from_seconds(0.15, TimerMode::Once),
}
}
}

// ·······
// Systems
// ·······

pub fn move_to(
mut cmd: Commands,
time: Res<Time>,
mut movables: Query<(Entity, &mut MoveTo, &mut Transform)>,
) {
for (entity, mut to, mut trans) in movables.iter_mut() {
let timer = to.timer.tick(time.delta());
if timer.just_finished() {
cmd.entity(entity).remove::<MoveTo>();
}
let t = timer.fraction();

let pos = if let Some(dir) = &to.bump_dir {
let offset = (t * PI).sin() * TILE_SEP;
to.start + dir_to_vec(dir, offset)
} else {
to.start.lerp(to.target, t)
};
trans.translation = pos.extend(trans.translation.z);
}
}

// ·······
// Helpers
// ·······

fn dir_to_vec(dir: &Direction, val: f32) -> Vec2 {
match dir {
Direction::North => Vec2::new(0., val),
Direction::South => Vec2::new(0., -val),
Direction::East => Vec2::new(val, 0.),
Direction::West => Vec2::new(-val, 0.),
}
}
Loading

0 comments on commit c585045

Please sign in to comment.