From 7e543a3021ef08e695b11f0b8f03e371a26d81b8 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Thu, 15 Jun 2023 00:11:39 -0500 Subject: [PATCH] Implement ground slam animation --- Crawler/Animation.h | 1 + Crawler/Crawler.cpp | 110 ++++++++++++++------ Crawler/Crawler.h | 10 +- Crawler/Crawler.vcxproj | 6 ++ Crawler/Crawler.vcxproj.filters | 12 +++ Crawler/DEFINES.h | 7 ++ Crawler/Effect.cpp | 34 ++++++ Crawler/Effect.h | 18 ++++ Crawler/Monster.h | 2 +- Crawler/Player.cpp | 15 +-- Crawler/assets/ground-slam-attack-back.png | Bin 912 -> 10762 bytes Crawler/assets/ground-slam-attack-front.png | Bin 812 -> 10392 bytes Crawler/cpp.hint | 9 ++ 13 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 Crawler/DEFINES.h create mode 100644 Crawler/Effect.cpp create mode 100644 Crawler/Effect.h create mode 100644 Crawler/cpp.hint diff --git a/Crawler/Animation.h b/Crawler/Animation.h index 96157475..9630d980 100644 --- a/Crawler/Animation.h +++ b/Crawler/Animation.h @@ -7,4 +7,5 @@ enum AnimationState{ BLUE_SLIME_IDLE,BLUE_SLIME_ROLL,BLUE_SLIME_JUMP,BLUE_SLIME_SPIT,BLUE_SLIME_DIE, RED_SLIME_IDLE,RED_SLIME_ROLL,RED_SLIME_JUMP,RED_SLIME_SPIT,RED_SLIME_DIE, YELLOW_SLIME_IDLE,YELLOW_SLIME_ROLL,YELLOW_SLIME_JUMP,YELLOW_SLIME_SPIT,YELLOW_SLIME_DIE, + GROUND_SLAM_ATTACK_BACK,GROUND_SLAM_ATTACK_FRONT, }; \ No newline at end of file diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 702c7ce7..f6fe667f 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -29,6 +29,8 @@ bool Crawler::OnUserCreate(){ GFX_Pl_Sheet.Load("assets/nico-warrior.png"); GFX_Slime_Sheet.Load("assets/slime.png"); GFX_Circle.Load("assets/circle.png"); + GFX_Effect_GroundSlam_Back.Load("assets/ground-slam-attack-back.png"); + GFX_Effect_GroundSlam_Front.Load("assets/ground-slam-attack-front.png"); //Animations InitializeAnimations(); @@ -52,6 +54,7 @@ bool Crawler::OnUserCreate(){ bool Crawler::OnUserUpdate(float fElapsedTime){ HandleUserInput(fElapsedTime); + UpdateEffects(fElapsedTime); player.Update(fElapsedTime); for(Monster&m:MONSTER_LIST){ m.Update(fElapsedTime); @@ -111,11 +114,18 @@ void Crawler::InitializeAnimations(){ ANIMATION_DATA[AnimationState(AnimationState::GREEN_SLIME_IDLE+state+slime*5)]=anim; } } + Animate2D::FrameSequence effect_groundslam_back(0.02f,Animate2D::Style::OneShot),effect_groundslam_front(0.02f,Animate2D::Style::OneShot); + for(int i=0;i<5;i++){ + effect_groundslam_back.AddFrame({&GFX_Effect_GroundSlam_Back,{{i*64,0},{64,64}}}); + effect_groundslam_front.AddFrame({&GFX_Effect_GroundSlam_Front,{{i*64,0},{64,64}}}); + } + ANIMATION_DATA[AnimationState::GROUND_SLAM_ATTACK_BACK]=effect_groundslam_back; + ANIMATION_DATA[AnimationState::GROUND_SLAM_ATTACK_FRONT]=effect_groundslam_front; } void Crawler::HandleUserInput(float fElapsedTime){ bool setIdleAnimation=true; - if(GetKey(SPACE).bPressed){ + if(GetKey(SPACE).bPressed&&player.GetState()==State::NORMAL){ player.Spin(Player::GROUND_SLAM_SPIN_TIME,14*PI); } if(GetKey(RIGHT).bHeld){ @@ -182,55 +192,55 @@ void Crawler::HandleUserInput(float fElapsedTime){ if(GetKey(UP).bHeld){ player.UpdateAnimation(AnimationState::WALK_N); } else - if(GetKey(DOWN).bHeld){ - player.UpdateAnimation(AnimationState::WALK_S); - } else - if(GetKey(LEFT).bHeld){ - player.UpdateAnimation(AnimationState::WALK_W); - } + if(GetKey(DOWN).bHeld){ + player.UpdateAnimation(AnimationState::WALK_S); + } else + if(GetKey(LEFT).bHeld){ + player.UpdateAnimation(AnimationState::WALK_W); + } } if(GetKey(LEFT).bReleased){ player.SetLastReleasedMovementKey(LEFT); if(GetKey(RIGHT).bHeld){ player.UpdateAnimation(AnimationState::WALK_E); } else - if(GetKey(DOWN).bHeld){ - player.UpdateAnimation(AnimationState::WALK_S); - } else - if(GetKey(UP).bHeld){ - player.UpdateAnimation(AnimationState::WALK_N); - } + if(GetKey(DOWN).bHeld){ + player.UpdateAnimation(AnimationState::WALK_S); + } else + if(GetKey(UP).bHeld){ + player.UpdateAnimation(AnimationState::WALK_N); + } } if(GetKey(DOWN).bReleased){ player.SetLastReleasedMovementKey(DOWN); if(GetKey(RIGHT).bHeld){ player.UpdateAnimation(AnimationState::WALK_E); } else - if(GetKey(UP).bHeld){ - player.UpdateAnimation(AnimationState::WALK_N); - } else - if(GetKey(LEFT).bHeld){ - player.UpdateAnimation(AnimationState::WALK_W); - } + if(GetKey(UP).bHeld){ + player.UpdateAnimation(AnimationState::WALK_N); + } else + if(GetKey(LEFT).bHeld){ + player.UpdateAnimation(AnimationState::WALK_W); + } } if(setIdleAnimation){ switch(player.GetLastReleasedMovementKey()){ - case UP:{ - player.UpdateAnimation(AnimationState::IDLE_N); - }break; - case DOWN:{ - player.UpdateAnimation(AnimationState::IDLE_S); - }break; - case LEFT:{ - player.UpdateAnimation(AnimationState::IDLE_W); - }break; - case RIGHT:{ - player.UpdateAnimation(AnimationState::IDLE_E); - }break; - default:{ - player.UpdateAnimation(AnimationState::IDLE_S); - } + case UP:{ + player.UpdateAnimation(AnimationState::IDLE_N); + }break; + case DOWN:{ + player.UpdateAnimation(AnimationState::IDLE_S); + }break; + case LEFT:{ + player.UpdateAnimation(AnimationState::IDLE_W); + }break; + case RIGHT:{ + player.UpdateAnimation(AnimationState::IDLE_E); + }break; + default:{ + player.UpdateAnimation(AnimationState::IDLE_S); + } } } else { //We have moved. @@ -243,6 +253,27 @@ void Crawler::UpdateCamera(float fElapsedTime){ view.SetWorldOffset(vi2d(camera.GetViewPosition())); } +void Crawler::UpdateEffects(float fElapsedTime){ + for(std::vector::iterator it=backgroundEffects.begin();it!=backgroundEffects.end();++it){ + Effect&e=*it; + if(!e.Update(fElapsedTime)){ + it=backgroundEffects.erase(it); + if(it==backgroundEffects.end()){ + break; + } + } + } + for(std::vector::iterator it=foregroundEffects.begin();it!=foregroundEffects.end();++it){ + Effect&e=*it; + if(!e.Update(fElapsedTime)){ + it=foregroundEffects.erase(it); + if(it==foregroundEffects.end()){ + break; + } + } + } +} + void Crawler::RenderWorld(float fElapsedTime){ Clear({100,180,100}); for (int x = view.GetTopLeftTile().x/24-1; x <= view.GetBottomRightTile().x/24; x++){ @@ -260,6 +291,9 @@ void Crawler::RenderWorld(float fElapsedTime){ vf2d shadowScale=vf2d{8/3.f,1}/std::max(1.f,player.GetZ()/4); view.DrawDecal(player.GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6},GFX_Circle.Decal(),shadowScale); } + for(Effect&e:backgroundEffects){ + e.Draw(); + } for(Monster&m:monstersBefore){ view.DrawPartialDecal(m.GetPos()-vf2d{12,12}*m.GetSizeMult(),m.GetFrame().GetSourceImage()->Decal(),m.GetFrame().GetSourceRect().pos,m.GetFrame().GetSourceRect().size,vf2d(m.GetSizeMult(),m.GetSizeMult())); } @@ -267,6 +301,9 @@ void Crawler::RenderWorld(float fElapsedTime){ for(Monster&m:monstersAfter){ view.DrawPartialDecal(m.GetPos()-vf2d{12,12}*m.GetSizeMult(),m.GetFrame().GetSourceImage()->Decal(),m.GetFrame().GetSourceRect().pos,m.GetFrame().GetSourceRect().size,vf2d(m.GetSizeMult(),m.GetSizeMult())); } + for(Effect&e:foregroundEffects){ + e.Draw(); + } for(std::vector::iterator it=DAMAGENUMBER_LIST.begin();it!=DAMAGENUMBER_LIST.end();++it){ DamageNumber&dn=*it; dn.lifeTime+=fElapsedTime; @@ -283,6 +320,11 @@ void Crawler::RenderWorld(float fElapsedTime){ } } +void Crawler::AddEffect(Effect foreground,Effect background){ + foregroundEffects.push_back(foreground); + backgroundEffects.push_back(background); +} + int main() { Crawler demo; diff --git a/Crawler/Crawler.h b/Crawler/Crawler.h index d23f41a7..b544f614 100644 --- a/Crawler/Crawler.h +++ b/Crawler/Crawler.h @@ -6,24 +6,28 @@ #include "olcPGEX_TransformedView.h" #include "Player.h" #include "olcUTIL_Camera2D.h" - +#include "Effect.h" class Crawler : public olc::PixelGameEngine { const vi2d WORLD_SIZE={64,8}; Camera2D camera; - TileTransformedView view; Player player; - Renderable GFX_Pl_Sheet,GFX_Slime_Sheet,GFX_Circle; + Renderable GFX_Pl_Sheet,GFX_Slime_Sheet,GFX_Circle, + GFX_Effect_GroundSlam_Back,GFX_Effect_GroundSlam_Front; + std::vectorforegroundEffects,backgroundEffects; public: Crawler(); public: + TileTransformedView view; bool OnUserCreate() override; bool OnUserUpdate(float fElapsedTime) override; void InitializeAnimations(); void HandleUserInput(float fElapsedTime); void UpdateCamera(float fElapsedTime); + void UpdateEffects(float fElapsedTime); void RenderWorld(float fElapsedTime); + void AddEffect(Effect foreground,Effect background); }; \ No newline at end of file diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index fcaa6f1a..88f5a0c6 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -132,6 +132,8 @@ + + @@ -144,11 +146,15 @@ + + + + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 172ceeb8..f51f3b17 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -48,6 +48,12 @@ Header Files + + Header Files + + + Header Files + @@ -68,5 +74,11 @@ Source Files + + Source Files + + + + \ No newline at end of file diff --git a/Crawler/DEFINES.h b/Crawler/DEFINES.h new file mode 100644 index 00000000..3982e148 --- /dev/null +++ b/Crawler/DEFINES.h @@ -0,0 +1,7 @@ +#pragma once +#define INCLUDE_ANIMATION_DATA extern std::mapANIMATION_DATA; +#define INCLUDE_MONSTER_LIST extern std::vectorMONSTER_LIST; +#define INCLUDE_SPAWNER_LIST extern std::vectorSPAWNER_LIST; +#define INCLUDE_DAMAGENUMBER_LIST extern std::vectorDAMAGENUMBER_LIST; +#define INCLUDE_game extern Crawler*game; +#define INCLUDE_MONSTER_DATA extern std::mapMONSTER_DATA; \ No newline at end of file diff --git a/Crawler/Effect.cpp b/Crawler/Effect.cpp new file mode 100644 index 00000000..a0422196 --- /dev/null +++ b/Crawler/Effect.cpp @@ -0,0 +1,34 @@ +#include "DEFINES.h" +#include "Effect.h" +#include "Crawler.h" + +INCLUDE_ANIMATION_DATA +INCLUDE_game + +Effect::Effect(vf2d pos,float lifetime,AnimationState animation,float fadeout) + :pos(pos),lifetime(lifetime),fadeout(fadeout),original_fadeoutTime(fadeout){ + this->animation.AddState(animation,ANIMATION_DATA[animation]); +} + +bool Effect::Update(float fElapsedTime){ + lifetime-=fElapsedTime; + if(lifetime<=0){ + fadeout-=fElapsedTime; + if(fadeout<=0){ + return false; + } + } + animation.UpdateState(internal_animState,fElapsedTime); +} + +void Effect::Draw(){ + if(fadeout==0){ + game->view.DrawPartialDecal(pos-GetFrame().GetSourceRect().size/2,GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size); + } else { + game->view.DrawPartialDecal(pos-GetFrame().GetSourceRect().size/2,GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,{1,1},{255,255,255,uint8_t(fadeout/original_fadeoutTime*255)}); + } +} + +Animate2D::Frame Effect::GetFrame(){ + return animation.GetFrame(internal_animState); +} \ No newline at end of file diff --git a/Crawler/Effect.h b/Crawler/Effect.h new file mode 100644 index 00000000..cef323ec --- /dev/null +++ b/Crawler/Effect.h @@ -0,0 +1,18 @@ +#pragma once +#include "olcPixelGameEngine.h" +#include "Animation.h" +#include "olcUTIL_Animate2D.h" + +struct Effect{ + vf2d pos; + float lifetime; + float fadeout; + Effect(vf2d pos,float lifetime,AnimationState animation,float fadeout=0.0f); + bool Update(float fElapsedTime); + Animate2D::Frame GetFrame(); + void Draw(); +private: + Animate2D::Animationanimation; + Animate2D::AnimationState internal_animState; + float original_fadeoutTime; +}; \ No newline at end of file diff --git a/Crawler/Monster.h b/Crawler/Monster.h index df0e2894..40ed98af 100644 --- a/Crawler/Monster.h +++ b/Crawler/Monster.h @@ -26,7 +26,7 @@ struct MonsterData{ MonsterName type; public: MonsterData(); - //When specifying animations, the first one will become the default animation. + //When specifying animations, the first one will become the default animation. The last becomes the death animation. MonsterData(MonsterName type,int hp,int atk,std::vectoranimations,float moveSpd=1.0f,float size=1.0f,MonsterStrategy strategy=RUN_TOWARDS); int GetHealth(); int GetAttack(); diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 17e2c265..1c45f43c 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -2,14 +2,14 @@ #include "Player.h" #include "Crawler.h" #include "DamageNumber.h" +#include "DEFINES.h" - -extern std::mapMONSTER_DATA; -extern std::vectorMONSTER_LIST; -extern std::mapANIMATION_DATA; -extern std::vectorSPAWNER_LIST; -extern std::vectorDAMAGENUMBER_LIST; -extern Crawler*game; +INCLUDE_MONSTER_DATA +INCLUDE_MONSTER_LIST +INCLUDE_ANIMATION_DATA +INCLUDE_SPAWNER_LIST +INCLUDE_DAMAGENUMBER_LIST +INCLUDE_game const float Player::GROUND_SLAM_SPIN_TIME=0.6f; @@ -116,6 +116,7 @@ void Player::Update(float fElapsedTime){ state=NORMAL; spin_angle=0; z=0; + game->AddEffect(Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_FRONT,0.6f},Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_BACK,0.6f}); } if(lastAnimationFlip>0){ lastAnimationFlip=std::max(0.f,lastAnimationFlip-fElapsedTime); diff --git a/Crawler/assets/ground-slam-attack-back.png b/Crawler/assets/ground-slam-attack-back.png index 6ab954451eac37f73d418eaeb62ed5b6d8c2920b..dda8c396c2ecf8dc33713d5282d4f024a82054be 100644 GIT binary patch literal 10762 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 OS!$^0Dpx7mhW{Ts?lL9- literal 912 zcmV;B18@9^P)Px&N=ZaPRCr$Pn6GjaK@i3_jwnuIk+>9301gU+#DT>j_Yqd&C|qzzObQ%%0_q$R z8+j;jNA;4XdS|<*|Loq}u{$wIZn}5p`}Nm7lbkgx|2kW_{-y$`4p=#X+5#&#Kpld$ z1!@bdoIq`Xl^dX*0!3Tk*Vk@;{X1VKiBPS=gLMs3U`Uz(m$c5D}WVTyAQA>6Fk4 zXait4f{Fof&XKYJ7*H+;L$S@%B5qb+m z0pwah1FG3SSN$9u#s$*>NSld@)ZC#}Us*qY@MNI*H~`W!J@UH-N?0!kKoE7^y^hFj z9<}d=c|ic_|053O07>Y@?rsRccJq_X8-dYUB$)%V0-&O!LM?PQZ(cOJw;%gZ3T6O9 za>(2OyrS=P4=ox;F~8eyYMuy#X3hlwg-ex|guZ%yr3&E!EpxF;0Hiwv<5&(k3d+rIY21O zu5J4j5PvM;90;~8x-Jd?$bEi!f1k7c{{R5o`}D&P|4mQ>fG&XV#{!XS0od23>zYXb z;2}8|pcPBc0!=vX3jl;?08O+&ppHm;H0lt~F_^w{@(@hPSs`$<^N<%I?tLIW?Ry$$ zIt4iE00el@A+NmNI{}TyTVYPT=@`^Dfgnx+2E7Vv!cmv%01W_LKL(75Xjcn(07x+C z=h2pt{v5VeTGBDA11zwpODypiGgaux0Pu(|5nJ2=B}94NQ2}^pEitw_V2M@rXsQnI m&{|?_b-)s<>d~}%2mApW?lf+_W#(4^0000aj^n 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