Note: a character I pasted in my previous attempt killed the post. I hope someone can delete that one. Sorry. :/
I'm writing a small sprite loading and management system for my game which reads sprite data out of Allegro config files which look like this:
#SelectExpand
1[still_up_1]
2x = 64
3y = 0
4w = 64
5h = 96
6
7[still_down_1]
8x = 192
9y = 0
10w = 64
11h = 96
12
13[still_left_1]
14x = 384
15y = 0
16w = 64
17h = 96
18
19[still_right_1]
20x = 256
21y = 480
22w = 64
23h = 96
24
25
26[walk_up_1]
27x = 0
28y = 0
29w = 64
30h = 96
31
32[walk_up_2]
33x = 64
34y = 0
35w = 64
36h = 96
37
38[walk_up_3]
39x = 128
40y = 0
41w = 64
42h = 96
I store that data in a series of structs as follows:
#SelectExpand
1const char *sprite_directions
[] = {"up",
"down",
"left",
"right", NULL
};
2const char *sprite_states
[] = { "still",
"walk", NULL
};
3
4typedef struct
5{
6 char state
[32]; // Textual identifier for animation (walk, run, etc)
7 char dir
[32]; // Direction (north, south, etc)
8 int cycle
; // Frame number
9 int x
; // Starting X of frame in bitmap
10 int y
; // duh
11 int h
; // Width on bitmap
12 int w
; // yeah
13} ANNE_SPRITE_FRAME
;
14
15typedef struct
16{
17 GList
*frame
;
18} ANNE_SPRITE_ANIMATIONS
;
19
20typedef struct
21{
22 ALLEGRO_BITMAP *bitmap
;
23 char name
[255];
24 ANNE_SPRITE_ANIMATIONS anim
;
25 ANNE_SPRITE_FRAME current
;
26} ANNE_SPRITE
;
As you can see, each frame has a direction, state, and animation index, and each state (walk, stand, run, etc) can have an indeterminate number of frames. I have several functions for working with these - to load a sprite:
#SelectExpand
1// Loads a sprite from the filesystem and into memory, and initializes a starting
2// frame.
3ANNE_SPRITE anne_sprite_load
(char sprite_name
[]){
4 ANNE_SPRITE
*sprite
= malloc(sizeof(*sprite
));
5 ANNE_SPRITE_ANIMATIONS
*anim
= malloc(sizeof(*anim
));
6
7 anim->frame
= NULL
;
8
9 int k
= 0;
10
11 char def_file_name
[255];
12 char image_file_name
[255];
13 char current_anim_state
[64];
14 char current_anim_frame
[128];
15
16 char state
[32];
17 char dir
[16];
18
19 int cycle
= 0;
20 int x
= 0;
21 int y
= 0;
22 int w
= 0;
23 int h
= 0;
24 srand(time(NULL
));
25
26 sprintf(def_file_name,
"data/sprites/%s.spr", sprite_name
);
27 sprintf(image_file_name,
"data/sprites/%s.png", sprite_name
);
28 sprintf(sprite->name,
"%s", sprite_name
);
29 sprintf(sprite->current.state,
"%s", sprite_states
[0]);
30 sprintf(sprite->current.dir,
"%s", sprite_directions
[0]);
31
32 // for convenience we use the config reader to parse the sprite definition file
33 ALLEGRO_CONFIG* sprite_definition
= al_load_config_file(def_file_name
);
34
35
36
37 for (int i
= 0; 1; i
++) {
38
39 if (sprite_states
[i
] == NULL
) { break; }
40 sprintf(current_anim_state,
"%s", sprite_states
[i
]);
41
42 #ifdef DEBUG
43 printf("i | %s\n", sprite_states
[i
]);
44 #endif
45
46 for (int j
= 0; 1; j
++) {
47 // The center loop simply iterates through the directions with the
48 // innermost loop.
49 if (sprite_directions
[j
] == NULL
) { break; }
50 for (k
= 1; 1; k
++) {
51 // In the innermost loop, we're stepping through a single state/direction
52 // pair and extracting the bitmap coordinates and dimensions and feeding them
53 // into a struct which we store in the animation.
54 //
55 // Let's get the current animation frame title from our lookups...
56 sprintf(current_anim_frame,
"%s_%s_%i", sprite_states
[i
], sprite_directions
[j
], k
);
57
58 // If it's missing any values, we can't render it properly. In this case, we break
59 // the current loop because chances are we've already read in all of this animation's
60 // frames.
61 if ( NULL
== al_get_config_value(sprite_definition, current_anim_frame,
"x") ||
62 NULL
== al_get_config_value(sprite_definition, current_anim_frame,
"y") ||
63 NULL
== al_get_config_value(sprite_definition, current_anim_frame,
"w") ||
64 NULL
== al_get_config_value(sprite_definition, current_anim_frame,
"h")
65 ) { break; }
66
67 sprintf(state,
"%s", sprite_states
[i
]);
68 sprintf(dir,
"%s", sprite_directions
[j
]);
69 x
= atoi(al_get_config_value(sprite_definition, current_anim_frame,
"x"));
70 y
= atoi(al_get_config_value(sprite_definition, current_anim_frame,
"y"));
71 w
= atoi(al_get_config_value(sprite_definition, current_anim_frame,
"w"));
72 h
= atoi(al_get_config_value(sprite_definition, current_anim_frame,
"h"));
73
74 ANNE_SPRITE_FRAME thisFrame
;
75 sprintf(thisFrame.state,
"%s", sprite_states
[i
]);
76 sprintf(thisFrame.dir,
"%s", dir
);
77 thisFrame.x
= x
;
78 printf("> x: %i | ", x
);
79 thisFrame.y
= y
;
80 thisFrame.w
= w
;
81 thisFrame.h
= h
;
82 thisFrame.cycle
= k
;
83
84
85 anim->frame
= g_list_append
(anim->frame,
&thisFrame
); // Prepend would be more performant but performance is irrelevant for sprite loading :)
86
87 #ifdef DEBUG
88 printf("j | state %s:%s, dir %s:%s | x %i:%i, y %i:%i, h %i:%i, w %i:%i, k %i:%i. | frame: %s\n",
89 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->state,state,
90 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->dir,dir,
91 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->x,x,
92 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->y,y,
93 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->h,h,
94 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->w,w,
95 ((ANNE_SPRITE_FRAME
*) anim->frame->data
)->cycle,k,
96 current_anim_frame
);
97 #endif
98 }
99 }
100 }
This appears to work correctly, and the debug printout at the end would appear to indicate that all the data is being stored correctly. However, when I try to read that data out later:
printf("frame | state: %s dir: %s cycle: %i x: %i y: %i w: %i h: %i\n",
((ANNE_SPRITE_FRAME *) search->data)->state,
((ANNE_SPRITE_FRAME *) search->data)->dir,
((ANNE_SPRITE_FRAME *) search->data)->cycle,
((ANNE_SPRITE_FRAME *) search->data)->x,
((ANNE_SPRITE_FRAME *) search->data)->y,
((ANNE_SPRITE_FRAME *) search->data)->w,
((ANNE_SPRITE_FRAME *) search->data)->h
);
I get garbage data like this:
#SelectExpand
1frame
| state:
(Redacted
) dir: right cycle:
-3446<form action id
="post_form" name
="post_form" method
="post" onsubmit
="return validate_post();"><form xmlns
="http://www.w3.org/1999/xhtml" action
="" id
="post_form" name
="post_form" method
="post" onsubmit
="return validate_post();">
2<input type
="hidden" name
="forum_id" value
="" />
3
4<table id
="table-add-post-inner" width
="800">
5<tbody>
6 <tr id
="tr-topic">
7 <td
class="label">Topic:
</td>
8 <td>
9<input name
="topic" type
="hidden" value
="Storing and reading sprite data from a linked list" />Storing
and reading sprite data from a linked list
</td>
10 </tr>
11 <tr id
="tr-icon">
12 <td
class="label">Icon:
</td>
13 <td>
14 <div>
15 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="default.gif" checked
="checked" /><img src
="/forums/icons/default.gif" width
="16" height
="16" alt
="default" />
16 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="c.gif" /><img src
="/forums/icons/c.gif" width
="16" height
="16" alt
="c" />
17 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="cpp.gif" /><img src
="/forums/icons/cpp.gif" width
="16" height
="16" alt
="c++" />
18 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="display.gif" /><img src
="/forums/icons/display.gif" width
="16" height
="16" alt
="display" />
19 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="joystick.gif" /><img src
="/forums/icons/joystick.gif" width
="16" height
="16" alt
="joystick" />
20 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="mad.gif" /><img src
="/forums/icons/mad.gif" width
="16" height
="16" alt
="mad" />
21 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="network.gif" /><img src
="/forums/icons/network.gif" width
="16" height
="16" alt
="network" />
22 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="note.gif" /><img src
="/forums/icons/note.gif" width
="16" height
="16" alt
="midi" />
23 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="question.gif" /><img src
="/forums/icons/question.gif" width
="16" height
="16" alt
="question" />
24 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="smile.gif" /><img src
="/forums/icons/smile.gif" width
="16" height
="16" alt
="smiley" />
25 <input type
="radio" name
="icon" class="checkbox" tabindex
="1" value
="speaker.gif" /><img src
="/forums/icons/speaker.gif" width
="16" height
="16" alt
="sound" />
26 </div>
27 </td>
28 </tr>
29 <tr id
="tr-message">
30 <td width
="100" valign
="top" class="label">Message:
</td>
31 <td width
="700">
32
33<div id
="post-988957-upload-button" class="upload-button" draggable
="true" style
="position: relative; overflow: hidden;">Drop Attachments Here
<input multiple
="multiple" style
="opacity: 0; position: absolute; top: 0; left: 0px; width: 500px; height: 32px; background: transparent; border: none;" type
="file" /></div>
34
35<div id
="post-988957-files" class="upload-files">
36
37
38</div>
39
40<input id
="btnPost" name
="submit_button" tabindex
="5" type
="submit" class="button" value
="Update Post" />
41
42<div class="mockup-box2"><div class="toolbar"><span
class="button"><img src
="/theme/default/icon/preview.png" /><span> Preview
</span>
</span>
<span
class="button"><img src
="/theme/default/icon/help.png" /><span> Formatting Help
</span>
</span>
</div>
<div class="container"><div class="compose" style
="visibility: visible;"><textarea id
="mub-body" class="mockup-box2" name
="body" tabindex
="2" rows
="20" cols
="50" style
="height: 348px;"></textarea>
</div>
<div class="preview" style
="visibility: hidden; display: block;"></div>
<input type
="hidden" name
="recovery_token" value
="post-988957" /><input type
="hidden" name
="attachments" value
="" /></div>
</div>
</td>
</tr>
</tbody>
</table>
</form>07570 x:
0 y:
-532077906 w:
-1434671328 h:
32604
43frame
| state:
(Redacted
) dir: right cycle:
-344607570 x:
0 y:
-532077906 w:
-1434671328 h:
32604
44frame
| state:
(Redacted
) dir: right cycle:
-344607570 x:
0 y:
-532077906 w:
-1434671328 h:
32604
My sanity check printf confirms that there are 17 frames in the frameset, as I expected, but I've been trying to confirm whether the junk output is due to a mistake saving the data or a mistake reading it since last night. If anyone could take a look, I'd really appreciate it.
I would like to note that I'm aware of hashmaps, and in particular I'm considering rewriting in order to take advantage of Sparsehash. There are a couple reasons why I'm putting that off.
Most importantly, I'm concerned about adding more code I don't understand, to replace a problem I've failed to diagnose. The flexibility of a linked list is also attractive and given the relatively small sets involved the performance penalty of iterating through them should be minimal.
Edit again: It appears I'm an idiot who allocated the variables on the stack.