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( |
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()) { |