Skip to content

Commit

Permalink
Implement actor facing
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrankel committed Jul 21, 2024
1 parent c64ccc2 commit d605473
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 30 deletions.
4 changes: 2 additions & 2 deletions assets/config/actor.ron
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
frames: [
SpriteAnimationFrame(index: 2, steps: 2),
SpriteAnimationFrame(index: 3, steps: 2),
]
)
],
),
),
},
player: "lucy",
Expand Down
59 changes: 35 additions & 24 deletions src/game/actor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
pub mod enemy;
pub mod facing;
pub mod player;

use bevy::ecs::system::EntityCommand;
use bevy::ecs::system::SystemState;
use bevy::prelude::*;
use bevy::utils::HashMap;
use facing::Facing;
use serde::Deserialize;
use serde::Serialize;

Expand All @@ -11,6 +16,8 @@ use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<ConfigHandle<ActorConfig>>();

app.add_plugins((enemy::plugin, facing::plugin, player::plugin));
}

#[derive(Asset, Reflect, Serialize, Deserialize)]
Expand Down Expand Up @@ -53,31 +60,39 @@ pub struct Actor {
pub texture_atlas_grid: TextureAtlasGrid,
#[serde(skip)]
pub texture_atlas_layout: Handle<TextureAtlasLayout>,
// TODO: Multiple animations per actor: HashMap<String, SpriteAnimation>?
pub sprite_animation: SpriteAnimation,
}

fn actor_helper(mut entity: EntityWorldMut, key: Option<String>) {
fn actor_helper(mut entity: EntityWorldMut, key: Option<String>) -> EntityWorldMut {
let config_handle = entity.world().resource::<ConfigHandle<ActorConfig>>();
let config = r!(entity
.world()
.resource::<Assets<ActorConfig>>()
.get(&config_handle.0));
let actor = r!(config.actors.get(key.as_ref().unwrap_or(&config.player)));
let config = r!(
entity,
entity
.world()
.resource::<Assets<ActorConfig>>()
.get(&config_handle.0),
);
let actor = r!(
entity,
config.actors.get(key.as_ref().unwrap_or(&config.player)),
);

entity.insert((
Name::new(actor.display_name.clone()),
SpriteBundle {
texture: actor.texture.clone(),
..default()
},
TextureAtlas {
layout: actor.texture_atlas_layout.clone(),
index: 0,
},
actor.sprite_animation.clone(),
));
entity.add(create_deck);
entity
.insert((
Name::new(actor.display_name.clone()),
SpriteBundle {
texture: actor.texture.clone(),
..default()
},
TextureAtlas {
layout: actor.texture_atlas_layout.clone(),
index: 0,
},
actor.sprite_animation.clone(),
Facing::default(),
))
.add(create_deck);
entity
}

pub fn actor(key: impl Into<String>) -> impl EntityCommand<World> {
Expand All @@ -86,7 +101,3 @@ pub fn actor(key: impl Into<String>) -> impl EntityCommand<World> {
actor_helper(entity, Some(key));
}
}

pub fn player(entity: EntityWorldMut) {
actor_helper(entity, None);
}
27 changes: 27 additions & 0 deletions src/game/actor/enemy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use bevy::ecs::system::EntityCommand;
use bevy::prelude::*;

use crate::game::actor::actor;
use crate::game::actor::facing::FacePlayer;
use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<IsEnemy>();
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct IsEnemy;

impl Configure for IsEnemy {
fn configure(app: &mut App) {
app.register_type::<Self>();
}
}

pub fn enemy(key: impl Into<String>) -> impl EntityCommand<World> {
let key = key.into();
move |mut entity: EntityWorldMut| {
entity.add(actor(key)).insert((IsEnemy, FacePlayer));
}
}
80 changes: 80 additions & 0 deletions src/game/actor/facing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use bevy::prelude::*;

use crate::core::camera::CameraRoot;
use crate::core::window::WindowRoot;
use crate::core::UpdateSet;
use crate::game::actor::player::IsPlayer;
use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<(Facing, FacePlayer, FaceCursor)>();
}

#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Facing(pub Dir2);

impl Configure for Facing {
fn configure(app: &mut App) {
app.register_type::<Facing>();
}
}

impl Default for Facing {
fn default() -> Self {
Self(Dir2::EAST)
}
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct FacePlayer;

impl Configure for FacePlayer {
fn configure(app: &mut App) {
app.register_type::<Self>();
app.add_systems(Update, face_player.in_set(UpdateSet::SyncEarly));
}
}

fn face_player(
player_query: Query<&GlobalTransform, With<IsPlayer>>,
mut facing_query: Query<(&mut Facing, &GlobalTransform), With<FacePlayer>>,
) {
let target_pos = r!(player_query.get_single()).translation().xy();

for (mut facing, gt) in &mut facing_query {
let pos = gt.translation().xy();
facing.0 = c!(Dir2::new(target_pos - pos));
}
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct FaceCursor;

impl Configure for FaceCursor {
fn configure(app: &mut App) {
app.register_type::<Self>();
app.add_systems(Update, face_cursor.in_set(UpdateSet::SyncEarly));
}
}

fn face_cursor(
window_root: Res<WindowRoot>,
window_query: Query<&Window>,
camera_root: Res<CameraRoot>,
camera_query: Query<(&Camera, &GlobalTransform)>,
mut facing_query: Query<(&mut Facing, &GlobalTransform), With<FaceCursor>>,
) {
let window = r!(window_query.get(window_root.primary));
let (camera, camera_gt) = r!(camera_query.get(camera_root.primary));
let target_pos = r!(window
.cursor_position()
.and_then(|cursor| camera.viewport_to_world_2d(camera_gt, cursor)));

for (mut facing, gt) in &mut facing_query {
let pos = gt.translation().xy();
facing.0 = c!(Dir2::new(target_pos - pos));
}
}
23 changes: 23 additions & 0 deletions src/game/actor/player.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use bevy::prelude::*;

use crate::game::actor::actor_helper;
use crate::game::actor::facing::FaceCursor;
use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<IsPlayer>();
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct IsPlayer;

impl Configure for IsPlayer {
fn configure(app: &mut App) {
app.register_type::<Self>();
}
}

pub fn player(entity: EntityWorldMut) {
actor_helper(entity, None).insert((IsPlayer, FaceCursor));
}
7 changes: 4 additions & 3 deletions src/screen/playing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use pyri_state::prelude::*;
use pyri_state::schedule::ResolveStateSet;

use crate::core::camera::CameraRoot;
use crate::game::actor;
use crate::game::actor::enemy::enemy;
use crate::game::actor::player::player;
use crate::screen::fade_in;
use crate::screen::Screen;
use crate::ui::prelude::*;
Expand Down Expand Up @@ -35,9 +36,9 @@ impl Configure for PlayingAssets {

fn enter_playing(mut commands: Commands) {
commands.spawn_with(fade_in);
commands.spawn_with(actor::player);
commands.spawn_with(player);
commands
.spawn_with(actor::actor("lucy"))
.spawn_with(enemy("lucy"))
.insert(TransformBundle::from_transform(
Transform::from_translation(vec3(10.0, 0.0, 0.0)),
));
Expand Down
9 changes: 8 additions & 1 deletion src/util/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ impl<T, E> Success<T> for Result<T, E> {
/// Unwrap or return.
#[macro_export]
macro_rules! r {
($expr:expr) => {
($return:expr, $expr:expr $(,)?) => {
match $crate::util::macros::Success::success($expr) {
Some(x) => x,
None => return $return,
}
};

($expr:expr $(,)?) => {
match $crate::util::macros::Success::success($expr) {
Some(x) => x,
None => return,
Expand Down

0 comments on commit d605473

Please sign in to comment.