Skip to content

Commit

Permalink
Added animation system to game
Browse files Browse the repository at this point in the history
  • Loading branch information
PurityLake committed Dec 5, 2023
1 parent 6b29a74 commit c50d1a0
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 60 deletions.
Binary file added assets/sprites/demon_die.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 117 additions & 9 deletions src/animation.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use std::{collections::HashMap, time::Duration};

use crate::json::*;
use bevy::prelude::*;
use serde::Deserialize;

#[derive(Component)]
pub struct AnimationIndicies {
pub first: usize,
pub last: usize,
}

pub struct AnimationLoadPlugin;

#[derive(Asset, TypePath, Debug, Deserialize, Default)]
Expand Down Expand Up @@ -36,26 +32,138 @@ pub struct AnimationList {
loaded: bool,
}

#[allow(dead_code)]
#[derive(Default, Clone)]
pub enum AnimState {
#[default]
Walking,
Dying,
Dead,
}

#[derive(Component, Clone, Default)]
pub struct AnimationComponent {
pub walk_handle: Handle<TextureAtlas>,
pub die_handle: Handle<TextureAtlas>,
pub first: usize,
pub last: usize,
pub name: String,
pub timer: Timer,
pub state: AnimState,
}

impl AnimationComponent {
fn new(
walk: Handle<TextureAtlas>,
die: Handle<TextureAtlas>,
name: String,
timer: Timer,
) -> Self {
Self {
walk_handle: walk,
die_handle: die,
first: 0,
last: 3,
name: name.clone(),
timer,
state: AnimState::default(),
}
}
}

#[derive(Resource, Default)]
pub struct EnemyAnimations {
pub enemies: HashMap<String, AnimationComponent>,
}

impl Plugin for AnimationLoadPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(JsonPlugin::<AnimationListAsset> {
extensions: vec!["animinfo.json"],
..default()
})
.init_resource::<AnimationList>()
.init_resource::<EnemyAnimations>()
.add_systems(Startup, setup)
.add_systems(OnEnter(crate::GameState::GamePlay), load_animations);
.add_systems(
Update,
load_animations.run_if(state_exists_and_equals(crate::GameState::GamePlay)),
)
.add_systems(
Update,
animate_sprite.run_if(state_exists_and_equals(crate::GameState::GamePlay)),
);
}
}

fn setup(asset_server: Res<AssetServer>, mut anim_list: ResMut<AnimationList>) {
anim_list.handle = asset_server.load("sprites/list.animinfo.json");
}

fn load_animations(list: ResMut<AnimationList>, anim_assets: ResMut<Assets<AnimationListAsset>>) {
fn load_animations(
list: ResMut<AnimationList>,
asset_server: Res<AssetServer>,
anim_assets: ResMut<Assets<AnimationListAsset>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut enemy_anims: ResMut<EnemyAnimations>,
) {
let anim_list = anim_assets.get(&list.handle);
if list.loaded || anim_list.is_none() {
return;
}
println!("{:?}", anim_list.unwrap());
let mut anim_map: HashMap<String, AnimationComponent> = HashMap::new();
for enemy in anim_list.unwrap().enemies.iter() {
let walk_texture_handle: Handle<Image> =
asset_server.load(format!("sprites/{0}_walk.png", enemy.name));
let die_texture_handle: Handle<Image> =
asset_server.load(format!("sprites/{0}_die.png", enemy.name));
let walk_texture_atlas = TextureAtlas::from_grid(
walk_texture_handle,
Vec2::new(enemy.walk.width as f32, enemy.walk.height as f32),
4,
1,
Some(Vec2::new(
enemy.walk.padding_x as f32,
enemy.walk.padding_y as f32,
)),
None,
);
let die_texture_atlas = TextureAtlas::from_grid(
die_texture_handle,
Vec2::new(enemy.die.width as f32, enemy.die.height as f32),
4,
1,
Some(Vec2::new(
enemy.die.padding_x as f32,
enemy.die.padding_y as f32,
)),
None,
);
anim_map.insert(
enemy.name.clone(),
AnimationComponent::new(
texture_atlases.add(walk_texture_atlas),
texture_atlases.add(die_texture_atlas),
enemy.name.clone(),
Timer::new(Duration::from_secs_f32(0.1), TimerMode::Repeating),
),
);
}
enemy_anims.enemies = anim_map;
}

fn animate_sprite(
time: Res<Time>,
mut query: Query<(&mut TextureAtlasSprite, &mut AnimationComponent)>,
) {
for (mut sprite, mut anim) in &mut query {
anim.timer.tick(time.delta());
if anim.timer.just_finished() {
sprite.index = if sprite.index == anim.last {
anim.first
} else {
sprite.index + 1
};
}
}
}
54 changes: 3 additions & 51 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ mod json;
mod player;
mod state;

use animation::{AnimationIndicies, AnimationLoadPlugin};
use animation::AnimationLoadPlugin;
use bevy::{asset::AssetMetaCheck, prelude::*, window::WindowTheme};
use enemy::EnemySpawnPlugin;
use player::PlayerPlugin;
use state::GameState;

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

fn main() {
App::new()
.insert_resource(AssetMetaCheck::Never)
Expand Down Expand Up @@ -47,58 +44,13 @@ fn main() {
.add_systems(Startup, setup)
.add_systems(
Update,
(main_menu_input, animate_sprite).run_if(in_state(GameState::MainMenu)),
(main_menu_input).run_if(in_state(GameState::MainMenu)),
)
.run();
}

fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
let texture_handle = asset_server.load("sprites/demon_walk.png");
let texture_atlas = TextureAtlas::from_grid(
texture_handle,
Vec2::new(12., 16.),
4,
1,
Some(Vec2::new(12., 0.)),
None,
);
let texture_atlas_handle = texture_atlases.add(texture_atlas);
let animation_indicies = AnimationIndicies { first: 0, last: 3 };
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
SpriteSheetBundle {
texture_atlas: texture_atlas_handle,
sprite: TextureAtlasSprite::new(animation_indicies.first),
transform: Transform::from_scale(Vec3::splat(6.0)),
..default()
},
animation_indicies,
AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
));
}

fn animate_sprite(
time: Res<Time>,
mut query: Query<(
&AnimationIndicies,
&mut AnimationTimer,
&mut TextureAtlasSprite,
)>,
) {
for (indicies, mut timer, mut sprite) in &mut query {
timer.tick(time.delta());
if timer.just_finished() {
sprite.index = if sprite.index == indicies.last {
indicies.first
} else {
sprite.index + 1
};
}
}
}

fn main_menu_input(input: Res<Input<KeyCode>>, mut game_state: ResMut<NextState<GameState>>) {
Expand Down

0 comments on commit c50d1a0

Please sign in to comment.