Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Player Animation on a keypress

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Player Animation on a keypress
Lasaqus7
Member #15,950
May 2015

Is there anywhere good sources where I can go and read to find out how to start to code my game so if I click a key (eg 1) that my player with cycle through his attack animation and continue to move in his current direction and only be able to attack within a certain timeframe ?

I have tried to look at other people code in games I found where the code was available but since they code with classes, it gets me confused alot as I am still learning.

I read this post https://www.allegro.cc/forums/thread/589018/634227#target. Although this kind of helps, for me it would mean putting a state within a state.

Is this possible ?

I currently have a CombatIdle state (Enemy sat idle), a Combat Chasing state (Enemy is chasing the player around) and a CombatRetreat state (Player out of range, enemy heading back to be idle). If I can put a state within a state, I'm guessing I could just put the state in the CombatChasing state as this is the one where the enemy would be in range.

I haven't posted code as I haven't began coding it. just looking for helpful resources on this before I come back and ask why it doesn't work ;D

Cheers

Dizzy Egg
Member #10,824
March 2009
avatar

...just show some code, and point out specifically what doesn't work the way you want it to....

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

Ok, I'm working on this now so once I get some code up and running I'll do that

Dizzy Egg
Member #10,824
March 2009
avatar

Ok cool! I guess basically it's going to come down to conditions every time your game loop starts again, so, did I press attack? No? Carry on then...or...yes? Is there an enemy next to me? No? just play my attack animation then....or....yes? Well, how many DP does it have?....etc etc etc

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

Ok, here's what I have so far

#SelectExpand
1enum STATE{MENU, PLAYING, COMBATSCREEN, COMBATIDLE, COMBATCHASING, COMBATRETREATING, ATTACKING, GAMEOVER}; 2enum KEYS{UP, DOWN, LEFT, RIGHT, ESCAPE, W, A, S, D, SPACE, C, KEY_1}; 3bool keys[] = { false, false, false, false, false, false, false, false, false, false, false, false}; 4 5Player Human; 6 7void PlayerAttack(Player &Human); 8 9int main(int argc, char **argv) 10{ 11 12 //ALLEGRO VARIABLES 13 ALLEGRO_DISPLAY *display = NULL; 14 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 15 ALLEGRO_TIMER *timer = NULL; 16 ALLEGRO_BITMAP *HumanImage; 17 18 //ALLEGRO INIT FUNCTIONS 19 if (!al_init()) //Initialize Allegro 20 return -1; 21 22 display = al_create_display(WIDTH, HEIGHT); //Create display object 23 24 if (!display) //Test display object 25 return -1; 26 27 //ADDON INSTALL 28 al_init_primitives_addon(); 29 al_install_keyboard(); 30 al_install_mouse(); 31 al_init_image_addon(); 32 al_init_font_addon(); 33 al_init_ttf_addon(); 34 35 //PROJECT INIT 36 event_queue = al_create_event_queue(); 37 timer = al_create_timer(1.0 / FPS); 38 39 HumanImage = al_load_bitmap("spritesheethuman.bmp"); 40 al_convert_mask_to_alpha(HumanImage, al_map_rgb(106, 76, 48)); 41 42 InitHuman(Human, HumanImage); 43 44 //TIMER INIT AND STARTUP 45 al_register_event_source(event_queue, al_get_keyboard_event_source()); 46 al_register_event_source(event_queue, al_get_mouse_event_source()); 47 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 48 al_register_event_source(event_queue, al_get_display_event_source(display)); 49 50 al_start_timer(timer); 51 while (!done) 52 { 53 ALLEGRO_EVENT ev; 54 al_wait_for_event(event_queue, &ev); 55 56 if (ev.type == ALLEGRO_EVENT_TIMER) 57 { 58 render = true; 59 60 //UPDATE=========================================== 61 if (keys[UP] || keys[W]) 62 MoveHumanUp(Human); 63 else if (keys[DOWN] || keys[S]) 64 MoveHumanDown(Human); 65 else if (keys[LEFT] || keys[A]) 66 MoveHumanLeft(Human); 67 else if (keys[RIGHT] || keys[D]) 68 MoveHumanRight(Human); 69 else if (keys[SPACE]) 70 { 71 72 } 73 //===================================================== 74 if (state == MENU) 75 { 76 } 77 else if (state == PLAYING) 78 { 79 } 80 else if (state == COMBATSCREEN) 81 { 82 } 83 else if (state == COMBATIDLE) 84 { 85 } 86 else if (state == COMBATCHASING) 87 { 88 if (threshold < CheckDistance(Orc.x, Orc.y, Orc.Startx, Orc.Starty)) 89 ChangeState(state, COMBATRETREATING); 90 else 91 { 92 float angle = AngleToTarget(Orc.x, Orc.y, Human.x, Human.y); 93 Orc.y += (Orc.speed * sin(angle)); 94 Orc.x += (Orc.speed * cos(angle)); 95 96 if (threshold < CheckDistance(Orc.x, Orc.y, Human.x, Human.y)) 97 ChangeState(state, COMBATRETREATING); 98 } 99 if (Orc.x > Human.x) 100 MoveOrcLeft(Orc); 101 else if (Orc.x < Human.x) 102 MoveOrcRight(Orc); 103 if (keys[KEY_1]) 104 PlayerAttack(Human); //Placed function here as best scenario for an attack to happen 105 } 106 else if (state == COMBATRETREATING) 107 { 108 } 109 } 110 else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 111 { 112 done = true; 113 } 114 115 else if (ev.type == ALLEGRO_EVENT_KEY_DOWN) 116 { 117 switch (ev.keyboard.keycode) 118 { 119 case ALLEGRO_KEY_1: 120 keys[KEY_1] = true; 121 break; 122 } 123 } 124 else if (ev.type == ALLEGRO_EVENT_KEY_UP) 125 { 126 switch (ev.keyboard.keycode) 127 { 128 case ALLEGRO_KEY_1: 129 keys[KEY_1] = false; 130 break; 131 } 132 } 133 134 //========================================================== 135 //RENDER 136 //========================================================== 137 if (render && al_is_event_queue_empty(event_queue)) 138 { 139 render = false; 140 141 else if (state == COMBATCHASING) 142 { 143 al_draw_bitmap(BattleBackground, 0, 0, 0); 144 DrawHuman(Human); 145 } 146 147 //FLIP BUFFERS ========================================= 148 al_flip_display(); 149 al_clear_to_color(al_map_rgb(0, 0, 0)); 150 } 151 } 152 return 0; 153} 154 155//Function for my attack 156void PlayerAttack(Player &Human) 157{ 158 Human.maxFrame = 13; 159 Human.frameCount = 0; 160 Human.animationRow = 0; 161 Human.animationColumns = 13; 162 Human.animationDirection = 1; 163 Human.frameDelay = 5; 164 Human.curFrame = 0; 165 keys[KEY_1] = true; 166 167 while (keys[KEY_1] && Human.curFrame != Human.maxFrame) //Does a loop as if 1 is held down 168 { 169 if (++Human.frameCount >= Human.frameDelay) //Cycles through frames at a speed based on the delay 170 { 171 Human.curFrame += Human.animationDirection; //Animation frames increased each cycle for new animation 172 Human.frameCount = 0; //Resets count to keep in touch with the delay 173 if (Human.curFrame >= Human.maxFrame) // If the last frame is reached stop animating 174 { 175 keys[KEY_1] = false; //1 is now up and back to game state(CHASING) 176 } 177 Human.x += Human.speed; //Player moves forward with each frame (may have to change this) 178 if (Human.x > WIDTH) //Stops player going past the end of the screen 179 Human.x = WIDTH; 180 } 181 } 182}

Again my code is too long to fit it all in so cut most of it out.

So what I believe is my code is actually performing an attack animation although it is too quick for me to see. All i see if my players jumping across the screen by X pixels

This may work, however I think I need some kind of timer to slow the animation down so instead of it playing faster than I can see, it maybe takes 1-1.5 secs to complete.

This I don't know how to do.

EDIT:- If you need my full code let me know and i'll try and post it somehow

Dizzy Egg
Member #10,824
March 2009
avatar

Ultimately I think you'll want a timer, but you can slow down things as well by using a 'miniCounter', that increments the frame, something like:

int miniFrame;
int mainFrame;

function movePlayer()
{
    miniFrame++;
    
    if(miniFrame > 1000) //increase/decrease this to slow down/speed up
    {
        mainFrame++;
    }
}

That way you can control when the frame count goes up by using the miniFrame counter, which can be a huge number - the only problem with this is that it wont run at the same speeds on different machines (probably), so not as precise as a timer, but it should slow things down enough to see the animation!

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

taronĀ 
Member #10,584
January 2009
avatar

Lasaqus7 said:

So what I believe is my code is actually performing an attack animation although it is too quick for me to see.

Shouldn't be the case, unless the whole animation happens in less than 16ms since it may end before the average screen can refresh.

while (keys[KEY_1] && Human.curFrame != Human.maxFrame) //Does a loop as if 1 is held down
{

This loop right here will do the whole attack in one frame without doing anything.

I wrote a pretty simple way to do animation based on your already existing code. (Not tested btw, just to give an idea of the logic you can possibly use)
Fix and change as you see fit.

#SelectExpand
1 2// A struct to keep track of Animation data. 3struct Animation 4{ 5 enum 6 { 7 IDLE, 8 MOVE, 9 ATTACK, 10 ... 11 }; 12 13 // Does the animation loop? 14 bool loop; 15 // Optionally add this if you want to be able to cancel animations. 16 // You might for example not want to allow the player 17 // to cancel a slide or attack but you do want to allow the player 18 // to cancel the idle animation. 19 // You could build a far more complex system with some animations 20 // allowing to be canceled by a few others but not all. 21 bool cancellable; 22 // Makes it easier to track whether or not the animation is still playing. 23 bool ended; 24 25 int maxFrame; 26 int frameCount; 27 int animationRow; 28 int animationColumns; 29 int animationDirection; 30 int frameDelay; 31 int curFrame; 32} 33 34struct Player 35{ 36 ... 37 // Hold the animation data; 38 Animation animation; 39 ... 40} 41 42// Always call this once per frame for each object with an animation. 43void UpdateAnimation(Animation &animation) 44{ 45 ++animation.frameCount; 46 if (animation.frameCount > animation.frameDelay) 47 { 48 ++animation.curFrame; 49 animation.frameCount = 0; 50 if (animation.curFrame >= animation.maxFrame) 51 { 52 if (animation.loop) 53 { 54 // Go back to the first frame if it should loop. 55 animation.curFrame = 0; 56 } 57 else 58 { 59 // Freeze the animation on the last frame. 60 // And flag it as ended. 61 animation.curFrame = animation.maxFrame; 62 animation.ended = true; 63 // Once the animation has ended assume 64 // that it may now be cancelled. 65 animation.cancellable = true; 66 } 67 } 68 } 69} 70 71// When you want to change the animation being played. 72// newAnimation is passed as a reference to a const Animation 73// to avoid accidentally writing to newAnimation instead of the 74// target animation. Not necessary. 75void ChangeAnimation(Animation &animation, const Animation &newAnimation) 76{ 77 // If the current animation can't be cancelled, do nothing. 78 // You might possibly want to change this to an assert. 79 // So the program terminates with an error instead of ignoring it. 80 if (animation.cancellable) 81 { 82 // Copy new animation here 83 // Alternatively remove this whole function and just do this assignment inline. 84 // But this might be more error-prone if you forget to check any important information 85 // about the animation like cancellable. 86 animation = newAnimation; 87 } 88} 89 90void PlayerAttack(Player &player) 91{ 92 if (!player.animation.cancellable) 93 { 94 // If the current animation can't be cancelled, do an early return 95 // and don't do any attack stuff. 96 // Might not want to allow the player to attack if he's been stunned for example. 97 return; 98 } 99 // *else* 100 // Create a new animation 101 Animation newAnimation; 102 103 // Initialize the new animation here 104 newAnimation.curFrame = 0; 105 ... 106 107 // Once the new animation has been initialized the player's animation can be changed 108 // to the new one. 109 ChangeAnimation(player.animation, newAnimation); 110 111 // Do whatever is necessary for dealing damage and all that here. 112 113}

Lasaqus7
Member #15,950
May 2015

Cheers for the help guys.

I'm working through your code taron, trying to understand it all, but I have ran into a few issues and questions.

#SelectExpand
1struct Animation 2{ 3 enum 4 { 5 IDLE, 6 MOVE, 7 ATTACK, 8 ... 9 };

How do I declare or reference these within my main code ? (I've tried animation.IDLE, animation[IDLE], Animation.animation[IDLE] but none work, unless as its a struct it works differently to the way I done my enum keys[].)
I'm assuming these would be where I code my variables for different animations. I haven't done this or seen this before.

Eg if ATTACK, use animationRow 1, and animationColumns = 12
if IDLE, use animation Row 2 etc.

Then I have added this into my Player struct

#SelectExpand
1struct Player 2{ 3 ... 4 // Hold the animation data; 5 Animation animation; 6 ... 7}

However I do get the following errors
C2146: syntax error: missing ';' before identifier 'animation'
C4430: missing type specifier - int assumed.Note C++ does not support default-int
C2039: 'animation' : is not a member of 'Player'(previously all my Player struct worked) And my Player struct is in a objects.h file, if this makes any difference.

I can place this in my main body of code although this would stop PlayerAttack from working as it would no longer reference to player (I think).

I do like the way you wrote this aswell, I understand most of it can see how it works and how it does the animations ;D

Dizzy Egg
Member #10,824
March 2009
avatar

You need a ; at the end of the struct!

struct Player
{
    Animation animation;
};

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

I copied that from taron, didn't see his didn't have a ; at the end of the struct.
In my code I do have this at the end.

Dizzy Egg
Member #10,824
March 2009
avatar

You haven't put ... in there have you??

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

No

My current player struct looks like this (this is working before starting to add in taron's code) I have added the Animation animation just to show how it would be

#SelectExpand
1struct Player 2{ 3 int ID; 4 int x; 5 int y; 6 int speed; 7 int boundx; 8 int boundy; 9 10 int dirX; 11 int dirY; 12 13 int maxFrame; 14 int curFrame; 15 int frameCount; 16 int frameDelay; 17 int frameWidth; 18 int frameHeight; 19 int animationColumns; 20 int animationDirection; 21 22 int animationRow; 23 24 int imageWidth; 25 int imageHeight; 26 27 int PlayerMaxHP; 28 int PlayerCurHP; 29 int PlayerMaxMana; 30 int PlayerCurMana; 31 int PlayerStr; 32 int PlayerInt; 33 int PlayerAgi; 34 int PlayerArmour; 35 int PlayerCrit; 36 int AttackCount; 37 38 int PlayerGold; 39 40 bool isAlive = true; 41 bool Attacking = false; 42 bool Crit = false; 43 44 ALLEGRO_BITMAP *image; 45 46 Animation animation; 47};

And then the error being displayed is attached

taronĀ 
Member #10,584
January 2009
avatar

I often forget to put a ';' after classes and structs if I recently programmed in a different language.

Lasaqus7 said:

How do I declare or reference these within my main code ? (I've tried animation.IDLE, animation[IDLE], Animation.animation[IDLE] but none work

Animation::IDLE would be the correct syntax.

Also you'd have to add int animationState or something inside the Animation struct to track which animation is being played, I forgot to add that.

Quote:

C2146: syntax error: missing ';' before identifier 'animation'
C4430: missing type specifier - int assumed.Note C++ does not support default-int

Generally this means it doesn't recognize the type. If you've put the Animation in its own header file, make sure to include that header in your Player header, otherwise the compiler will not be able to find the definition of the type.
If you've defined the Animation in your main.cpp or whatever file, move it to a header file.
So it would look something like this:

#ifndef PLAYER_H
#define PLAYER_H

#include "animation.h"

struct Player
{
   ...
};

#endif

Or the shorter but less portable way. (Non standard extension, but supported by many compilers)

#pragma once
#include "animation.h"

struct Player
{
   ...
};

Dizzy Egg
Member #10,824
March 2009
avatar

Yeah the code is fine, have you declared Animation before player?

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

Nope, I had declared Animation after it, changed it and that's fixed that part, thanks Dizzy.

Taron, thanks for the help on the correct syntax, now to work on getting it right, not long left to finish it

All my structs are in the same file, I only have 2 objects.h (all my structs) and main.cpp

Hopefully I should be able to figure this out now.

Dizzy Egg
Member #10,824
March 2009
avatar

Cool! Let us know if you get stuck again....I'm warming up for Speedhack so need motivation to get coding Allegro5 again! ;D

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

I think i'm doing this wrong :-(

#SelectExpand
1else if (state == COMBATIDLE) 2 { 3 if (threshold > CheckDistance(Human.x, Human.y, Orc.x, Orc.y)) 4 { 5 ChangeState(state, COMBATCHASING); 6 } 7 8 if (keys[KEY_1]) 9 { 10 ChangeAnimation(animation, newAnimation); <-- This errors - identifier "newAnimation" is undefined 11 } 12 }

I have placed my ChangeAnimation in my ALLEGRO_EVENT_TIMER code, so to me it says, if Player is in the CombatIdle state and KEY_1 is pressed then change the animation.

#SelectExpand
1void ChangeAnimation(Animation &animation, const Animation &newAnimation) 2{ 3 if (animation.cancellable) 4 { 5 if (Animation::IDLE) 6 { 7 animation.maxFrame = 1; 8 animation.animationRow = 1; 9 animation.animationColumns = 1; 10 animation.cancellable = true; 11 animation.loop = false; 12 animation.animationState = 1; 13 } 14 else if (Animation::ATTACK) 15 { 16 animation.maxFrame = 13; 17 animation.animationRow = 0; 18 animation.animationColumns = 13; 19 animation.cancellable = false; 20 animation.loop = false; 21 animation.animationState = 2; 22 } 23 animation = newAnimation; 24 } 25}

Then for the function I have placed some states of the animations.

What would go in place of newAnimation in this line ChangeAnimation(animation, newAnimation); ?

I tried Animation::ATTACK, animation::ATTACK, animation.ATTACK with no luck.

Dizzy Egg
Member #10,824
March 2009
avatar

Before you use 'newAnimation', do you create it?

ie:

Animation *newAnimation;

ChangeAnimation(animation, newAnimation);

(the above code wont work, I was just seeing if you'd 'created' the newAnimation...)

EDIT:

Consider this...

int i = 5;

void changeInt(int &arg)
{
     arg = 20;
}

int main()
{
    changeInt(i);
    
    //i now = 20!!
}

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

That will be it, no I haven't created it apart from in the ChangeAnimation function. I see that part is in the PlayerAttack part of tarons code.

I feel i'm getting more and more confused as I delve further into the depths of the unknown :-) And going from zero Allegro5 experience to doing it alot everyday is also confusing me.

Cheers Dizzy :-)

Dizzy Egg
Member #10,824
March 2009
avatar

Try not to go too far down a road that is suggested on here if it seems alien; I used 1 .cpp file and hundreds of separate variables for my first 20 odd projects....LONG before I started delving into enums and structs....keep it simple, and as soon as something doesn't make sense, come on here and post some code...that's how I learned!

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

Cheers for the advice.

So trying to do it in a way I understand. Using Mike Geig's tutorial on his spaceshooter he has a comet going across his screen using no input. It move across without any keys being involved and his code for that is

#SelectExpand
1void UpdateComet(Comet comets[], int size) 2{ 3 for(int i = 0; i < size; i++) //His comet array size, so for every comet available 4 { 5 if(comets[i].live) //Is the comet on screen then do the below 6 { 7 if(++comets[i].frameCount >= comets[i].frameDelay) 8 { 9 comets[i].curFrame += comets[i].animationDirection; 10 if(comets[i].curFrame >= comets[i].maxFrame) 11 comets[i].curFrame = 0; 12 else if( comets[i].curFrame <= 0) 13 comets[i].curFrame = comets[i].maxFrame - 1; 14 15 comets[i].frameCount = 0; // Do all the updating and frame cycling to appear as if it is moving 16 } 17 18 comets[i].x -= comets[i].speed; //Move the comet 19 } 20 } 21}

I added comments to compare to my code.

So based on that, I want a animation to happen basically the same way except I press a key first so I press the key and declare that the player is attacking

#SelectExpand
1void PlayerAttack(Player &Human) 2{ 3 Human.Attacking = true; 4if (Human.Attacking) // Is my player attacking, then do the code below 5 { 6 ++Human.frameCount; 7 if (Human.frameCount > Human.frameDelay) 8 { 9 ++Human.curFrame; 10 Human.frameCount = 0; 11 if (Human.curFrame >= Human.maxFrame) // All the frame cycling stuff as above 12 { 13 Human.curFrame = Human.maxFrame; 14 Human.Attacking = false; //Has the animation reached the end frame, if so player stops attacking and end the function 15 } 16 } 17 }

So looking at the 2 codes, why is one allowing it to be cycled all the time and perform the animation across the screen, yet with my code only cycle through the animation when I actually hold down the key. From what I gather I have removed the keypress part by adding the PlayerAttacking, so my PlayerAttacking is now similar to If the comet is onscreen.

The only big difference I see is
My code

#SelectExpand
1if (keys[KEY_1]) 2 PlayerAttack(Human);

Mike Geig's code

#SelectExpand
1UpdateComet(comets, NUM_COMETS);

We both have our calls in the ALLEGRO_EVENT_TIMER section aswell.

EDIT_ OMG I got it to work
On the bad side is when I move after attacking it plays that animation again, but I got it to work ;D

Dizzy Egg
Member #10,824
March 2009
avatar

So...do you want your animation run whilst you're holding down KEY_1, or simply run it when KEY_1 is pressed?

EDIT!

YOU GOT IT! 8-) ;D

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

I just edited at the same time but I kinda got it to work

I just wanted my player to move about, but if the 1 key was pressed (and released) it would play a attack animation.

As of now when I press 1 it does plays the attack animation

However when I move after that it keeps playing the attack animation instead of the moving ones. So need to fix that

But it is progress

Dizzy Egg
Member #10,824
March 2009
avatar

Cool :P

EDIT:

Human.Attacking = true;
if (Human.Attacking) // Is my player attacking, then do the code below

..hmmmmm ;)

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

Lasaqus7
Member #15,950
May 2015

You right

I changed to this

#SelectExpand
1if (keys[KEY_1]) 2Human.Attacking = true; 3 4if (Human.Attacking) 5PlayerAttack(Human);

Where before I was doing

#SelectExpand
1if (keys[KEY_1]) 2PlayerAttack(Human);

Then putting Human.Attacking = true; in my function.

It was probably suggested way earlier than now I just didn't get it in the right place

EDIT:- If press 1 while moving he plays his attack animation all the time, until i release left or right and repress it :(

 1   2 


Go to: