|
This thread is locked; no one can reply to it. |
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 Cheers |
Dizzy Egg
Member #10,824
March 2009
|
...just show some code, and point out specifically what doesn't work the way you want it to....
---------------------------------------------------- |
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
|
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
---------------------------------------------------- |
Lasaqus7
Member #15,950
May 2015
|
Ok, here's what I have so far 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
|
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!
---------------------------------------------------- |
l j
Member #10,584
January 2009
|
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) 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. 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[].) Eg if ATTACK, use animationRow 1, and animationColumns = 12 Then I have added this into my Player struct 1struct Player
2{
3 ...
4 // Hold the animation data;
5 Animation animation;
6 ...
7}
However I do get the following errors 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 |
Dizzy Egg
Member #10,824
March 2009
|
You need a ; at the end of the struct! struct Player { Animation animation; };
---------------------------------------------------- |
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. |
Dizzy Egg
Member #10,824
March 2009
|
You haven't put ... in there have you??
---------------------------------------------------- |
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 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 |
l j
Member #10,584
January 2009
|
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' 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. #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
|
Yeah the code is fine, have you declared Animation before player?
---------------------------------------------------- |
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
|
Cool! Let us know if you get stuck again....I'm warming up for Speedhack so need motivation to get coding Allegro5 again!
---------------------------------------------------- |
Lasaqus7
Member #15,950
May 2015
|
I think i'm doing this wrong :-( 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. 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
|
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!! }
---------------------------------------------------- |
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
|
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!
---------------------------------------------------- |
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 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 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 1if (keys[KEY_1])
2 PlayerAttack(Human);
Mike Geig's code 1UpdateComet(comets, NUM_COMETS);
We both have our calls in the ALLEGRO_EVENT_TIMER section aswell. EDIT_ OMG I got it to work |
Dizzy Egg
Member #10,824
March 2009
|
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!
---------------------------------------------------- |
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
|
Cool EDIT: Human.Attacking = true; if (Human.Attacking) // Is my player attacking, then do the code below ..hmmmmm
---------------------------------------------------- |
Lasaqus7
Member #15,950
May 2015
|
You right I changed to this 1if (keys[KEY_1])
2Human.Attacking = true;
3
4if (Human.Attacking)
5PlayerAttack(Human);
Where before I was doing 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
|