test/snake/snake.c

changeset 234
a1d70b8018c1
parent 233
bda74199223f
child 238
e22abcd22e47
equal deleted inserted replaced
233:bda74199223f 234:a1d70b8018c1
59 enum MoveDirection target_direction; 59 enum MoveDirection target_direction;
60 /** 60 /**
61 * The speed in tiles per second. 61 * The speed in tiles per second.
62 */ 62 */
63 float speed; 63 float speed;
64 } Spaceship; 64 asc_vec2i new_position;
65 bool reset_position;
66 } Player;
65 67
66 static const unsigned game_field_size = 16; 68 static const unsigned game_field_size = 16;
67 static const unsigned game_field_tile_size = 32; 69 static const unsigned game_field_tile_size = 32;
68 70
69 static void init_globals(void) { 71 static void globals_init(void) {
70 asc_transform_identity(rotations[MOVE_UP]); 72 asc_transform_identity(rotations[MOVE_UP]);
71 asc_transform_roll(rotations[MOVE_LEFT], asc_rad(-90)); 73 asc_transform_roll(rotations[MOVE_LEFT], asc_rad(-90));
72 asc_transform_roll(rotations[MOVE_RIGHT], asc_rad(90)); 74 asc_transform_roll(rotations[MOVE_RIGHT], asc_rad(90));
73 asc_transform_roll(rotations[MOVE_DOWN], asc_rad(180)); 75 asc_transform_roll(rotations[MOVE_DOWN], asc_rad(180));
74 directions[MOVE_UP] = ASC_VEC2I(0, -1); 76 directions[MOVE_UP] = ASC_VEC2I(0, -1);
75 directions[MOVE_LEFT] = ASC_VEC2I(-1, 0); 77 directions[MOVE_LEFT] = ASC_VEC2I(-1, 0);
76 directions[MOVE_DOWN] = ASC_VEC2I(0, 1); 78 directions[MOVE_DOWN] = ASC_VEC2I(0, 1);
77 directions[MOVE_RIGHT] = ASC_VEC2I(1, 0); 79 directions[MOVE_RIGHT] = ASC_VEC2I(1, 0);
78 } 80 }
79 81
80 static void destroy_textures(void) { 82 static void textures_destroy(void) {
81 asc_texture_destroy(tex2d, TEX2D_COUNT); 83 asc_texture_destroy(tex2d, TEX2D_COUNT);
82 } 84 }
83 85
84 static void init_textures(void) { 86 static void textures_init(void) {
85 asc_texture_init_2d(tex2d, TEX2D_COUNT); 87 asc_texture_init_2d(tex2d, TEX2D_COUNT);
86 asc_texture_from_file(TEXTURE_SHIP, "ship.png"); 88 asc_texture_from_file(TEXTURE_SHIP, "ship.png");
87 asc_texture_from_file(TEXTURE_BACKDROP, "backdrop.png"); 89 asc_texture_from_file(TEXTURE_BACKDROP, "backdrop.png");
88 asc_gl_context_add_cleanup_func(asc_active_glctx, destroy_textures); 90 asc_gl_context_add_cleanup_func(asc_active_glctx, textures_destroy);
89 } 91 }
90 92
91 static void scale_backdrop(AscBehavior *behavior) { 93 static void backdrop_scale(AscBehavior *behavior) {
92 // scale the backdrop to the size of the window 94 // scale the backdrop to the size of the window
93 if (asc_active_window->resized) { 95 if (asc_active_window->resized) {
94 asc_ptr_cast(AscSprite, sprite, behavior->node); 96 asc_ptr_cast(AscSprite, sprite, behavior->node);
95 asc_vec2u window_size = asc_active_window->dimensions; 97 asc_vec2u window_size = asc_active_window->dimensions;
96 asc_sprite_set_size(sprite, window_size); 98 asc_sprite_set_size(sprite, window_size);
97 } 99 }
98 } 100 }
99 101
100 static void create_backdrop(void) { 102 static void backdrop_create(void) {
101 AscSceneNode *node = asc_sprite( 103 AscSceneNode *node = asc_sprite(
102 .texture = TEXTURE_BACKDROP, 104 .texture = TEXTURE_BACKDROP,
103 .texture_scale_mode = ASC_TEXTURE_SCALE_REPEAT, 105 .texture_scale_mode = ASC_TEXTURE_SCALE_REPEAT,
104 ); 106 );
105 asc_behavior_add(node, .func = scale_backdrop); 107 asc_behavior_add(node, .func = backdrop_scale);
106 asc_scene_add_node(BACKDROP_SCENE, node); 108 asc_scene_add_node(BACKDROP_SCENE, node);
107 } 109 }
108 110
109 static void update_fps_counter(AscBehavior *behavior) { 111 static void fps_counter_update(AscBehavior *behavior) {
110 asc_ptr_cast(AscText, node, behavior->node); 112 asc_ptr_cast(AscText, node, behavior->node);
111 static float last_fps = 0.f; 113 static float last_fps = 0.f;
112 if (fabsf(asc_context.frame_rate - last_fps) > 1) { 114 if (fabsf(asc_context.frame_rate - last_fps) > 1) {
113 last_fps = asc_context.frame_rate; 115 last_fps = asc_context.frame_rate;
114 asc_text_printf(node, "%.2f FPS", asc_context.frame_rate); 116 asc_text_printf(node, "%.2f FPS", asc_context.frame_rate);
115 } 117 }
116 } 118 }
117 119
118 static void tie_fps_counter_to_corner(AscBehavior *behavior) { 120 static void fps_counter_tie_to_corner(AscBehavior *behavior) {
119 // TODO: this should be replaced with some sort of UI layout manager 121 // TODO: this should be replaced with some sort of UI layout manager
120 AscSceneNode *node = behavior->node; 122 AscSceneNode *node = behavior->node;
121 if (asc_test_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED) || asc_active_window->resized) { 123 if (asc_test_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED) || asc_active_window->resized) {
122 asc_vec2u bottom_right = asc_active_window->dimensions; 124 asc_vec2u bottom_right = asc_active_window->dimensions;
123 asc_vec2u text_size = ((AscText*)node)->dimension; 125 asc_vec2u text_size = ((AscText*)node)->dimension;
126 (int) bottom_right.y - (int) text_size.height - 10 128 (int) bottom_right.y - (int) text_size.height - 10
127 )); 129 ));
128 } 130 }
129 } 131 }
130 132
131 static void create_fps_counter(void) { 133 static void fps_counter_create(void) {
132 AscSceneNode *node = asc_text( 134 AscSceneNode *node = asc_text(
133 .name = "FPS Counter", 135 .name = "FPS Counter",
134 .color = ASC_RGB(255, 255, 255), 136 .color = ASC_RGB(255, 255, 255),
135 .font = asc_font(ASC_FONT_REGULAR, 12), 137 .font = asc_font(ASC_FONT_REGULAR, 12),
136 ); 138 );
137 asc_behavior_add(node, .func = update_fps_counter, .interval = asc_seconds(1)); 139 asc_behavior_add(node, .func = fps_counter_update, .interval = asc_seconds(1));
138 asc_behavior_add(node, tie_fps_counter_to_corner); 140 asc_behavior_add(node, fps_counter_tie_to_corner);
139 asc_ui_add_node(node); 141 asc_ui_add_node(node);
140 } 142 }
141 143
142 static void create_score_counter(void) { 144 static void score_counter_create(void) {
143 AscSceneNode *node = asc_text( 145 AscSceneNode *node = asc_text(
144 .name = "Score Counter", 146 .name = "Score Counter",
145 .x = 10, .y = 10, 147 .x = 10, .y = 10,
146 .text = "Score: 0", 148 .text = "Score: 0",
147 .color = ASC_RGB(0, 255, 0), 149 .color = ASC_RGB(0, 255, 0),
148 .font = asc_font(ASC_FONT_BOLD, 16), 150 .font = asc_font(ASC_FONT_BOLD, 16),
149 ); 151 );
150 asc_ui_add_node(node); 152 asc_ui_add_node(node);
151 } 153 }
152 154
153 static void move_spaceship(AscBehavior *behavior) { 155 static void player_move(AscBehavior *behavior) {
154 AscSceneNode *node = behavior->node; 156 AscSceneNode *node = behavior->node;
155 Spaceship *spaceship = node->user_data; 157 Player *player = node->user_data;
158
159 // check if the position is set programmatically
160 if (player->reset_position) {
161 asc_scene_node_set_position2f(node,
162 ASC_VEC2F(
163 game_field_tile_size * player->new_position.x,
164 game_field_tile_size * player->new_position.y));
165 player->reset_position = false;
166 return;
167 }
168
169 // normal movement
156 const int ts = (int) game_field_tile_size; 170 const int ts = (int) game_field_tile_size;
157 const float fts = (float) ts; 171 const float fts = (float) ts;
158 const float speed = fts * spaceship->speed * asc_context.frame_factor; 172 const float speed = fts * player->speed * asc_context.frame_factor;
159 const asc_vec2i dir = directions[spaceship->direction]; 173 const asc_vec2i dir = directions[player->direction];
160 const asc_vec2f movement = asc_vec2f_scale(asc_vec2_itof(dir), speed); 174 const asc_vec2f movement = asc_vec2f_scale(asc_vec2_itof(dir), speed);
161 // check if we are supposed to change the direction 175 // check if we are supposed to change the direction
162 if (spaceship->direction == spaceship->target_direction) { 176 if (player->direction == player->target_direction) {
163 // move without changing the direction 177 // move without changing the direction
164 asc_scene_node_move2f(node, movement); 178 asc_scene_node_move2f(node, movement);
165 } else { 179 } else {
166 // determine axis 180 // determine axis
167 // and check if we are about to cross the center 181 // and check if we are about to cross the center
180 } 194 }
181 if (rotate) { 195 if (rotate) {
182 // snap position to the center of the tile 196 // snap position to the center of the tile
183 node->position.x = roundf(node->position.x / fts) * fts; 197 node->position.x = roundf(node->position.x / fts) * fts;
184 node->position.y = roundf(node->position.y / fts) * fts; 198 node->position.y = roundf(node->position.y / fts) * fts;
185 spaceship->direction = spaceship->target_direction; 199 player->direction = player->target_direction;
186 asc_scene_node_set_rotation(node, rotations[spaceship->direction]); 200 asc_scene_node_set_rotation(node, rotations[player->direction]);
187 } else { 201 } else {
188 // changing the direction not permitted, yet, continue movement 202 // changing the direction not permitted, yet, continue movement
189 asc_scene_node_move2f(node, movement); 203 asc_scene_node_move2f(node, movement);
190 } 204 }
191 } 205 }
192 } 206 }
193 207
194 static Spaceship *create_spaceship(void) { 208 static void player_position(Player *pl, int x, int y) {
209 pl->new_position.x = x;
210 pl->new_position.y = y;
211 pl->reset_position = true;
212 }
213
214 static Player *player_create(void) {
195 AscSceneNode *sprite = asc_sprite( 215 AscSceneNode *sprite = asc_sprite(
196 .name = "Player", 216 .name = "Player",
197 .texture = TEXTURE_SHIP, 217 .texture = TEXTURE_SHIP,
198 // TODO: introduce a function to set the position of a space ship
199 .x = game_field_tile_size * 8,
200 .y = game_field_tile_size * 12,
201 .width = game_field_tile_size, 218 .width = game_field_tile_size,
202 .height = game_field_tile_size, 219 .height = game_field_tile_size,
203 .origin_x = game_field_tile_size / 2, 220 .origin_x = game_field_tile_size / 2,
204 .origin_y = game_field_tile_size / 2, 221 .origin_y = game_field_tile_size / 2,
205 ); 222 );
206 asc_scene_add_node(MAIN_SCENE, sprite); 223 asc_scene_add_node(MAIN_SCENE, sprite);
207 Spaceship *ship = asc_scene_node_allocate_data(sprite, sizeof(Spaceship)); 224 Player *player = asc_scene_node_allocate_data(sprite, sizeof(Player));
208 ship->speed = 2.f; // start with 2 tiles/sec 225 player_position(player, 12, 8);
209 asc_behavior_add(sprite, move_spaceship); 226 player->speed = 2.f; // start with 2 tiles/sec
210 return sprite->user_data; 227 asc_behavior_add(sprite, player_move);
211 } 228 return player;
212 229 }
213 static void create_gamefield() { 230
231 static void gamefield_create() {
214 // TODO: create a proper data structure and a more interesting map than just a basic grid 232 // TODO: create a proper data structure and a more interesting map than just a basic grid
215 AscSceneNode *gamefield = asc_scene_node_empty(); 233 AscSceneNode *gamefield = asc_scene_node_empty();
216 for (unsigned x = 1; x <= game_field_size; x++) { 234 for (unsigned x = 1; x <= game_field_size; x++) {
217 for (unsigned y = 1; y <= game_field_size; y++) { 235 for (unsigned y = 1; y <= game_field_size; y++) {
218 AscSceneNode *tile = asc_rectangle( 236 AscSceneNode *tile = asc_rectangle(
227 } 245 }
228 asc_scene_node_set_zindex(gamefield, -2); 246 asc_scene_node_set_zindex(gamefield, -2);
229 asc_scene_add_node(MAIN_SCENE, gamefield); 247 asc_scene_add_node(MAIN_SCENE, gamefield);
230 } 248 }
231 249
232 static asc_rect update_viewport_for_window_resize(asc_vec2u window_size) { 250 static asc_rect main_scene_viewport_update(asc_vec2u window_size) {
233 251
234 // margins 252 // margins
235 const unsigned margin = 10; 253 const unsigned margin = 10;
236 254
237 // space for score, power-ups, etc. 255 // space for score, power-ups, etc.
273 "Fatal Error",asc_get_error(), NULL); 291 "Fatal Error",asc_get_error(), NULL);
274 return 1; 292 return 1;
275 } 293 }
276 294
277 // initialize globals 295 // initialize globals
278 init_globals(); 296 globals_init();
279 297
280 // create the window 298 // create the window
281 AscWindowSettings settings; 299 AscWindowSettings settings;
282 asc_window_settings_init_defaults(&settings); 300 asc_window_settings_init_defaults(&settings);
283 asc_window_initialize(0, &settings); 301 asc_window_initialize(0, &settings);
284 asc_window_set_title(0, "Snake"); 302 asc_window_set_title(0, "Snake");
285 float ui_scale = asc_ui_scale_auto(); 303 float ui_scale = asc_ui_scale_auto();
286 asc_window_set_size(0, ASC_VEC2U(700+ui_scale*200, 700)); 304 asc_window_set_size(0, ASC_VEC2U(700+ui_scale*200, 700));
287 305
288 // load textures 306 // load textures
289 init_textures(); 307 textures_init();
290 308
291 // initialize the scenes 309 // initialize the scenes
292 asc_scene_init(BACKDROP_SCENE, "backdrop", 310 asc_scene_init(BACKDROP_SCENE, "backdrop",
293 .type = ASC_CAMERA_ORTHO, 311 .type = ASC_CAMERA_ORTHO,
294 .projection_update_func = asc_camera_ortho_update_size 312 .projection_update_func = asc_camera_ortho_update_size
299 (game_field_size+1)*game_field_tile_size, 317 (game_field_size+1)*game_field_tile_size,
300 (game_field_size+1)*game_field_tile_size 318 (game_field_size+1)*game_field_tile_size
301 ), 319 ),
302 .viewport_clear = true, 320 .viewport_clear = true,
303 .clear_color = ASC_RGB(0, 128, 90), 321 .clear_color = ASC_RGB(0, 128, 90),
304 .viewport_update_func = update_viewport_for_window_resize 322 .viewport_update_func = main_scene_viewport_update
305 ); 323 );
306 324
307 // backdrop for letterbox/pillarbox 325 // backdrop for letterbox/pillarbox
308 create_backdrop(); 326 backdrop_create();
309 327
310 // the game field 328 // the game field
311 create_gamefield(); 329 gamefield_create();
312 330
313 // create UI elements 331 // create UI elements
314 create_fps_counter(); 332 fps_counter_create();
315 create_score_counter(); 333 score_counter_create();
316 334
317 // create spaceship 335 // create spaceship
318 Spaceship *spaceship = create_spaceship(); 336 Player *spaceship = player_create();
319 337
320 // Main Loop 338 // Main Loop
321 do { 339 do {
322 // quit application on any error 340 // quit application on any error
323 if (asc_has_error()) { 341 if (asc_has_error()) {

mercurial