Okay, so after getting a lot of help from this board, I managed to get my basic fight stance animation working for my sword fighting game. After that, I figured all my other animations would be easy. I did my running forward animation and it worked fine. Now I tried using the same procedure to put in my running backward animation and I'm not getting good results. When I pass what should be the correct number into the function, it skips the last frame of the animation. When I pass 4 into the function (what should be one too high), it displays that last frame, but there's a stuttering glitch that comes in every second or two. Note: I used the same exact method to put it in as I did the last two sprite sheets, and there are four frames on the sprite sheet for the problematic animation.
1 | // Sword Play |
2 | |
3 | // Known issues: |
4 | |
5 | // flawlessly, player can move forward...and that's it |
6 | // moving backward has animation issues |
7 | // many more sprite sheets still needed |
8 | |
9 | #include <allegro.h> |
10 | |
11 | #define BLACK makecol(0, 0, 0) |
12 | #define GREEN makecol(0, 255, 0) |
13 | #define WHITE makecol(255, 255, 255) |
14 | |
15 | volatile int frame1 = 0, frame2 = 0, p1 = 40, p2 = SCREEN_W - 40, player, stamina1 = 100, stamina2 = 100, ticks = 0; |
16 | const int frameRate = 8, staticy = 200; |
17 | |
18 | // obtains sprites from a sprite sheet |
19 | BITMAP *grabframe(BITMAP *source, int width, int height, int startx, int starty, int columns, int frame) |
20 | { |
21 | BITMAP *temp = create_bitmap(width, height); |
22 | |
23 | int x = startx + (frame % columns) * width; |
24 | int y = starty + (frame / columns) * height; |
25 | |
26 | blit(source, temp, x, y, 0, 0, width, height); |
27 | |
28 | return temp; |
29 | } |
30 | BITMAP *buffer; // used to smooth out the animation |
31 | BITMAP *buffer2; // used to smooth out animation even more |
32 | BITMAP *pic; // temporary variable for putting sprites into an array |
33 | BITMAP *SwordPlayTitle; // title screen picture |
34 | |
35 | // sprite arrays end in s (for sprite) to differentiate from functions |
36 | BITMAP *fightStances[4]; |
37 | BITMAP *runForwards[9]; |
38 | BITMAP *runBackwards[4]; |
39 | |
40 | /*BITMAP *attackHeads[10]; |
41 | BITMAP *attackHands[10]; |
42 | BITMAP *attackFoots[10]; |
43 | BITMAP *defendHeads[1]; |
44 | BITMAP *defendHands[1]; |
45 | BITMAP *defendFoots[1]; |
46 | |
47 | BITMAP *powerStruggles[1]; |
48 | BITMAP *pushs[4];*/ |
49 | |
50 | void time(void); // used to count hundredths of a second |
51 | void frame1Function(int maxframes); // controls animation of current action for player 1 |
52 | |
53 | void runForward(int player); // 9 frames |
54 | void runBackward(int player); // 4 frames |
55 | |
56 | /*void attackHead(int player); |
57 | void attackHand(int player); |
58 | void attackFoot(int player); |
59 | void defendHead(int player); |
60 | void defendHand(int player); |
61 | void defendFoot(int player); |
62 | |
63 | void push(int player); // 5 frames: one player stumbles backward, other stays in fightStance |
64 | void powerStruggle(void); // both players go through same two frames, then loser is pushed*/ |
65 | |
66 | void stamina(void); // displays players' stamina bars |
67 | void increaseStamina(void); // slightly regenerates stamina every second |
68 | /*void decreaseStamina(void); // decreases player's stamina when an attack is blocked*/ |
69 | |
70 | int main(void) |
71 | { |
72 | allegro_init(); // uses Allegro |
73 | install_timer(); // initializes timing functionality |
74 | install_keyboard(); // allows use of keyboard |
75 | set_color_depth(32); // sets color depth for pallette of sprites |
76 | set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); // makes the screen a 640x480 window |
77 | SwordPlayTitle = load_bitmap("SwordPlayTitle.bmp", NULL); // loads title screen image |
78 | |
79 | blit(SwordPlayTitle, screen, 0, 0, 0, 0, SwordPlayTitle->w, SwordPlayTitle->h); // puts the title screen picture on the screen |
80 | textprintf_centre_ex(screen, font, SCREEN_W / 2, 460, makecol(255, 255, 255), -1, "Press Any Key to Continue..."); |
81 | destroy_bitmap(SwordPlayTitle); // frees up memory by destroying a now unneeded title screen |
82 | readkey(); // waits for input to proceed |
83 | |
84 | rest(50); // brief pause between input and loading the actual game |
85 | |
86 | // loads frames for the fighting stance animation |
87 | pic = load_bitmap("FightStanceSpriteSheet.bmp", NULL); |
88 | for(int x = 0; x <= 3; x++) |
89 | { |
90 | fightStances[x] = grabframe(pic, 240, 240, 0, 0, 4, x); |
91 | } |
92 | |
93 | // loads frames for the running forward animation |
94 | pic = load_bitmap("RunForwardSheet.bmp", NULL); |
95 | for(int x = 0; x <= 8; x++) |
96 | { |
97 | runForwards[x] = grabframe(pic, 240, 240, 0, 0, 4, x); |
98 | } |
99 | |
100 | // loads frames for the running backward animation |
101 | pic = load_bitmap("RunBackwardSheet.bmp", NULL); |
102 | for(int x = 0; x <= 3; x++) |
103 | { |
104 | runBackwards[x] = grabframe(pic, 240, 240, 0, 0, 4, x); |
105 | } |
106 | |
107 | destroy_bitmap(pic); // frees up memory by destroying a now unneeded temporary variable |
108 | buffer = create_bitmap(SCREEN_W, SCREEN_H); // makes the buffer |
109 | buffer2 = create_bitmap(SCREEN_W, SCREEN_H); // makes the buffer's buffer |
110 | install_int_ex(time, BPS_TO_TIMER(100)); // calls the timer function 100 times every second |
111 | install_int_ex(increaseStamina, BPS_TO_TIMER(1)); // increases the stamina bars for both players by 1 every second |
112 | |
113 | // main game loop |
114 | while(!key[KEY_ESC]) |
115 | { |
116 | clear_to_color(buffer, WHITE); |
117 | /*// attack commands for player 1 |
118 | if(key[KEY_W] && key[KEY_SPACE]) |
119 | attackHead(1); |
120 | else if(key[KEY_SPACE]) |
121 | attackHand(1); |
122 | else if(key[KEY_S] && key[KEY_SPACE]) |
123 | attackFoot(1); |
124 | |
125 | // defend commands for player 1 |
126 | else if(key[KEY_W] && key[KEY_LSHIFT]) |
127 | defendHead(1); |
128 | else if(key[KEY_LSHIFT]) |
129 | defendHand(1); |
130 | else if(key[KEY_S] && key[KEY_LSHIFT]) |
131 | defendFoot(1);*/ |
132 | |
133 | // movement commands for player 1 |
134 | if(key[KEY_D]) |
135 | { |
136 | runForward(1); |
137 | masked_blit(runForwards[frame1], buffer, 0, 0, p1, staticy, runForwards[frame1]->w, runForwards[frame1]->h); |
138 | } |
139 | else if(key[KEY_A]) |
140 | { |
141 | runBackward(1); |
142 | masked_blit(runBackwards[frame1], buffer, 0, 0, p1, staticy, runBackwards[frame1]->w, runBackwards[frame1]->h); |
143 | } |
144 | else |
145 | { |
146 | frame1Function(3); |
147 | masked_blit(fightStances[frame1], buffer, 0, 0, p1, staticy, fightStances[frame1]->w, fightStances[frame1]->h); |
148 | } |
149 | stamina(); // displays the stamina bars on the screen |
150 | masked_blit(buffer, buffer2, 0, 0, 0, 0, buffer->w, buffer->h); // puts everything that just happened in the last split-second on the second buffer |
151 | masked_blit(buffer2, screen, 0, 0, 0, 0, buffer2->w, buffer2->h); // puts that buffer on the screen |
152 | clear_bitmap(buffer); // erases the buffer so that it can be changed again |
153 | } |
154 | remove_int(increaseStamina); // frees up the memory used to call this function over and over again |
155 | |
156 | return 0; // makes this code compatible with non-MS compilers |
157 | }END_OF_MAIN() |
158 | |
159 | void time() |
160 | { |
161 | ticks++; |
162 | |
163 | if(ticks >= 100) |
164 | ticks = 0; |
165 | } |
166 | |
167 | void frame1Function(int maxframes) |
168 | { |
169 | if(frame1 >= maxframes) |
170 | frame1 = 0; |
171 | |
172 | float comparisonVariable = 100 / frameRate; |
173 | |
174 | if(ticks >= comparisonVariable) |
175 | { |
176 | ticks = 0; |
177 | |
178 | if(frame1 >= maxframes) |
179 | frame1 = 0; |
180 | else |
181 | frame1++; |
182 | } |
183 | } |
184 | |
185 | void runForward(int player) |
186 | { |
187 | if(player == 1) |
188 | { |
189 | frame1Function(8); |
190 | |
191 | if(p1 <= (SCREEN_W - 240)) |
192 | p1++; |
193 | } |
194 | } |
195 | |
196 | void runBackward(int player) |
197 | { |
198 | if(player == 1) |
199 | { |
200 | frame1Function(4); |
201 | |
202 | if(p1 > 0) |
203 | p1--; |
204 | } |
205 | } |
206 | |
207 | void stamina() |
208 | { |
209 | textprintf_ex(buffer, font, 5, 15, BLACK, -1, "Player 1"); |
210 | textprintf_right_ex(buffer, font, SCREEN_W - 5, 15, BLACK, -1, "Player 2"); |
211 | rectfill(buffer, 5, 5, 5 + stamina1, 10, GREEN); |
212 | rectfill(buffer, SCREEN_W - 105, 5, SCREEN_W - 105 + stamina2, 10, GREEN); |
213 | } |
214 | |
215 | void increaseStamina() |
216 | { |
217 | stamina1++; |
218 | if(stamina1 > 100) |
219 | stamina1 = 100; |
220 | stamina2++; |
221 | if(stamina2 > 100) |
222 | stamina2 = 100; |
223 | } |
Your frame1Function(...) logic is faulty - if frame1 is 3, then you can reach the frame1++ line, setting frame1 to 4, which exceeds your array dimension, before dropping out of the function. Do a check of frame1 before the function exits.
Alternatively, since you're passing the value 8 (one less than the array size) in runForward(...), you should be passing 3 runBackward(...).
I said that when I pass in the correct value, it skips the last frame. Also, it checks for when it is greater than or equal to, not just greater than, so it will only increase it when it is supposed to.
Sigh. Your function is wrong. You're thinking along the right lines, but it's not implemented correctly. I'll explain why by stepping through your frame1Function. Below I've put line numbers in function, and have a couple of scenarios to show you where the function falls over.
void frame1Function(int maxframes) { 1 if(frame1 >= maxframes) 2 frame1 = 0; 3 float comparisonVariable = 100 / frameRate; 4 if(ticks >= comparisonVariable) { 5 ticks = 0; 6 if(frame1 >= maxframes) 7 frame1 = 0; 8 else 9 frame1++; } }
We'll set frame1 = 3 & call frame1Function(3):
The condition in line 1 frame1 >= maxframes is true so line 2 is executed
> frame1 is now 0
If line 4 evaluates to true, then lines 5-9 are executed
> Line 6 evaluates to false, so line 9 is executed
> frame++ sets frame to 1
Hey, guess what, you've skipped a frame. frame1 went from 3 to 1, and missed out 0.
Ok, let's try out frame1 = 3 & call frame1Function(4):
Line 1 evaluates to false, so line 2 is not executed
> frame1 is still 3
If line 4 evaluates to true, then lines 5-9 are executed
> Line 6 evaluates to false, so line 9 is executed
> frame++ sets frame to 4
Hey, guess what, you've gone past the array boundary. frame1 went from 3 to 4.
To correct this behaviour, use the modulus operator ('%'), as in the following code:
void frame1Function(int maxframes) { if(ticks >= (100 / frameRate)) { ticks = 0; frame1++; } frame1 = frame1 % maxframes; }