1 | /******************************************************************************************* |
2 | * |
3 | * raylib - sample game: asteroids survival |
4 | * |
5 | * Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria |
6 | * |
7 | * This game has been created using raylib v1.3 (www.raylib.com) |
8 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) |
9 | * |
10 | * Copyright (c) 2015 Ramon Santamaria (@raysan5) |
11 | * |
12 | ********************************************************************************************/ |
13 | |
14 | #include "raylib.h" |
15 | |
16 | #include <math.h> |
17 | |
18 | #if defined(PLATFORM_WEB) |
19 | #include <emscripten/emscripten.h> |
20 | #endif |
21 | |
22 | //---------------------------------------------------------------------------------- |
23 | // Some Defines |
24 | //---------------------------------------------------------------------------------- |
25 | #define PLAYER_BASE_SIZE 20.0f |
26 | #define PLAYER_SPEED 6.0f |
27 | #define PLAYER_MAX_SHOOTS 10 |
28 | |
29 | #define METEORS_SPEED 2 |
30 | #define MAX_MEDIUM_METEORS 8 |
31 | #define MAX_SMALL_METEORS 16 |
32 | |
33 | //---------------------------------------------------------------------------------- |
34 | // Types and Structures Definition |
35 | //---------------------------------------------------------------------------------- |
36 | typedef struct Player { |
37 | Vector2 position; |
38 | Vector2 speed; |
39 | float acceleration; |
40 | float rotation; |
41 | Vector3 collider; |
42 | Color color; |
43 | } Player; |
44 | |
45 | typedef struct Meteor { |
46 | Vector2 position; |
47 | Vector2 speed; |
48 | float radius; |
49 | bool active; |
50 | Color color; |
51 | } Meteor; |
52 | |
53 | //------------------------------------------------------------------------------------ |
54 | // Global Variables Declaration |
55 | //------------------------------------------------------------------------------------ |
56 | static const int screenWidth = 800; |
57 | static const int screenHeight = 450; |
58 | |
59 | static int framesCounter = 0; |
60 | static bool gameOver = false; |
61 | static bool pause = false; |
62 | |
63 | // NOTE: Defined triangle is isosceles with common angles of 70 degrees. |
64 | static float shipHeight = 0.0f; |
65 | |
66 | static Player player = { 0 }; |
67 | static Meteor mediumMeteor[MAX_MEDIUM_METEORS] = { 0 }; |
68 | static Meteor smallMeteor[MAX_SMALL_METEORS] = { 0 }; |
69 | |
70 | //------------------------------------------------------------------------------------ |
71 | // Module Functions Declaration (local) |
72 | //------------------------------------------------------------------------------------ |
73 | static void InitGame(void); // Initialize game |
74 | static void UpdateGame(void); // Update game (one frame) |
75 | static void DrawGame(void); // Draw game (one frame) |
76 | static void UnloadGame(void); // Unload game |
77 | static void UpdateDrawFrame(void); // Update and Draw (one frame) |
78 | |
79 | //------------------------------------------------------------------------------------ |
80 | // Program main entry point |
81 | //------------------------------------------------------------------------------------ |
82 | int main(void) |
83 | { |
84 | // Initialization (Note windowTitle is unused on Android) |
85 | //--------------------------------------------------------- |
86 | InitWindow(screenWidth, screenHeight, "sample game: asteroids survival" ); |
87 | |
88 | InitGame(); |
89 | |
90 | #if defined(PLATFORM_WEB) |
91 | emscripten_set_main_loop(UpdateDrawFrame, 0, 1); |
92 | #else |
93 | SetTargetFPS(60); |
94 | //-------------------------------------------------------------------------------------- |
95 | |
96 | // Main game loop |
97 | while (!WindowShouldClose()) // Detect window close button or ESC key |
98 | { |
99 | // Update and Draw |
100 | //---------------------------------------------------------------------------------- |
101 | UpdateDrawFrame(); |
102 | //---------------------------------------------------------------------------------- |
103 | } |
104 | #endif |
105 | // De-Initialization |
106 | //-------------------------------------------------------------------------------------- |
107 | UnloadGame(); // Unload loaded data (textures, sounds, models...) |
108 | |
109 | CloseWindow(); // Close window and OpenGL context |
110 | //-------------------------------------------------------------------------------------- |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | //------------------------------------------------------------------------------------ |
116 | // Module Functions Definitions (local) |
117 | //------------------------------------------------------------------------------------ |
118 | |
119 | // Initialize game variables |
120 | void InitGame(void) |
121 | { |
122 | int posx, posy; |
123 | int velx, vely; |
124 | bool correctRange = false; |
125 | |
126 | pause = false; |
127 | |
128 | framesCounter = 0; |
129 | |
130 | shipHeight = (PLAYER_BASE_SIZE/2)/tanf(20*DEG2RAD); |
131 | |
132 | // Initialization player |
133 | player.position = (Vector2){screenWidth/2, screenHeight/2 - shipHeight/2}; |
134 | player.speed = (Vector2){0, 0}; |
135 | player.acceleration = 0; |
136 | player.rotation = 0; |
137 | player.collider = (Vector3){player.position.x + sin(player.rotation*DEG2RAD)*(shipHeight/2.5f), player.position.y - cos(player.rotation*DEG2RAD)*(shipHeight/2.5f), 12}; |
138 | player.color = LIGHTGRAY; |
139 | |
140 | for (int i = 0; i < MAX_MEDIUM_METEORS; i++) |
141 | { |
142 | posx = GetRandomValue(0, screenWidth); |
143 | |
144 | while(!correctRange) |
145 | { |
146 | if (posx > screenWidth/2 - 150 && posx < screenWidth/2 + 150) posx = GetRandomValue(0, screenWidth); |
147 | else correctRange = true; |
148 | } |
149 | |
150 | correctRange = false; |
151 | |
152 | posy = GetRandomValue(0, screenHeight); |
153 | |
154 | while(!correctRange) |
155 | { |
156 | if (posy > screenHeight/2 - 150 && posy < screenHeight/2 + 150) posy = GetRandomValue(0, screenHeight); |
157 | else correctRange = true; |
158 | } |
159 | |
160 | correctRange = false; |
161 | velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
162 | vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
163 | |
164 | while(!correctRange) |
165 | { |
166 | if (velx == 0 && vely == 0) |
167 | { |
168 | velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
169 | vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
170 | } |
171 | else correctRange = true; |
172 | } |
173 | mediumMeteor[i].position = (Vector2){posx, posy}; |
174 | mediumMeteor[i].speed = (Vector2){velx, vely}; |
175 | mediumMeteor[i].radius = 20; |
176 | mediumMeteor[i].active = true; |
177 | mediumMeteor[i].color = GREEN; |
178 | } |
179 | |
180 | for (int i = 0; i < MAX_SMALL_METEORS; i++) |
181 | { |
182 | posx = GetRandomValue(0, screenWidth); |
183 | |
184 | while(!correctRange) |
185 | { |
186 | if (posx > screenWidth/2 - 150 && posx < screenWidth/2 + 150) posx = GetRandomValue(0, screenWidth); |
187 | else correctRange = true; |
188 | } |
189 | |
190 | correctRange = false; |
191 | |
192 | posy = GetRandomValue(0, screenHeight); |
193 | |
194 | while(!correctRange) |
195 | { |
196 | if (posy > screenHeight/2 - 150 && posy < screenHeight/2 + 150) posy = GetRandomValue(0, screenHeight); |
197 | else correctRange = true; |
198 | } |
199 | |
200 | correctRange = false; |
201 | velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
202 | vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
203 | |
204 | while(!correctRange) |
205 | { |
206 | if (velx == 0 && vely == 0) |
207 | { |
208 | velx = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
209 | vely = GetRandomValue(-METEORS_SPEED, METEORS_SPEED); |
210 | } |
211 | else correctRange = true; |
212 | } |
213 | smallMeteor[i].position = (Vector2){posx, posy}; |
214 | smallMeteor[i].speed = (Vector2){velx, vely}; |
215 | smallMeteor[i].radius = 10; |
216 | smallMeteor[i].active = true; |
217 | smallMeteor[i].color = YELLOW; |
218 | } |
219 | } |
220 | |
221 | // Update game (one frame) |
222 | void UpdateGame(void) |
223 | { |
224 | if (!gameOver) |
225 | { |
226 | if (IsKeyPressed('P')) pause = !pause; |
227 | |
228 | if (!pause) |
229 | { |
230 | framesCounter++; |
231 | |
232 | // Player logic |
233 | |
234 | // Rotation |
235 | if (IsKeyDown(KEY_LEFT)) player.rotation -= 5; |
236 | if (IsKeyDown(KEY_RIGHT)) player.rotation += 5; |
237 | |
238 | // Speed |
239 | player.speed.x = sin(player.rotation*DEG2RAD)*PLAYER_SPEED; |
240 | player.speed.y = cos(player.rotation*DEG2RAD)*PLAYER_SPEED; |
241 | |
242 | // Controller |
243 | if (IsKeyDown(KEY_UP)) |
244 | { |
245 | if (player.acceleration < 1) player.acceleration += 0.04f; |
246 | } |
247 | else |
248 | { |
249 | if (player.acceleration > 0) player.acceleration -= 0.02f; |
250 | else if (player.acceleration < 0) player.acceleration = 0; |
251 | } |
252 | if (IsKeyDown(KEY_DOWN)) |
253 | { |
254 | if (player.acceleration > 0) player.acceleration -= 0.04f; |
255 | else if (player.acceleration < 0) player.acceleration = 0; |
256 | } |
257 | |
258 | // Movement |
259 | player.position.x += (player.speed.x*player.acceleration); |
260 | player.position.y -= (player.speed.y*player.acceleration); |
261 | |
262 | // Wall behaviour for player |
263 | if (player.position.x > screenWidth + shipHeight) player.position.x = -(shipHeight); |
264 | else if (player.position.x < -(shipHeight)) player.position.x = screenWidth + shipHeight; |
265 | if (player.position.y > (screenHeight + shipHeight)) player.position.y = -(shipHeight); |
266 | else if (player.position.y < -(shipHeight)) player.position.y = screenHeight + shipHeight; |
267 | |
268 | // Collision Player to meteors |
269 | player.collider = (Vector3){player.position.x + sin(player.rotation*DEG2RAD)*(shipHeight/2.5f), player.position.y - cos(player.rotation*DEG2RAD)*(shipHeight/2.5f), 12}; |
270 | |
271 | for (int a = 0; a < MAX_MEDIUM_METEORS; a++) |
272 | { |
273 | if (CheckCollisionCircles((Vector2){player.collider.x, player.collider.y}, player.collider.z, mediumMeteor[a].position, mediumMeteor[a].radius) && mediumMeteor[a].active) gameOver = true; |
274 | } |
275 | |
276 | for (int a = 0; a < MAX_SMALL_METEORS; a++) |
277 | { |
278 | if (CheckCollisionCircles((Vector2){player.collider.x, player.collider.y}, player.collider.z, smallMeteor[a].position, smallMeteor[a].radius) && smallMeteor[a].active) gameOver = true; |
279 | } |
280 | |
281 | // Meteor logic |
282 | |
283 | for (int i = 0; i < MAX_MEDIUM_METEORS; i++) |
284 | { |
285 | if (mediumMeteor[i].active) |
286 | { |
287 | // movement |
288 | mediumMeteor[i].position.x += mediumMeteor[i].speed.x; |
289 | mediumMeteor[i].position.y += mediumMeteor[i].speed.y; |
290 | |
291 | // wall behaviour |
292 | if (mediumMeteor[i].position.x > screenWidth + mediumMeteor[i].radius) mediumMeteor[i].position.x = -(mediumMeteor[i].radius); |
293 | else if (mediumMeteor[i].position.x < 0 - mediumMeteor[i].radius) mediumMeteor[i].position.x = screenWidth + mediumMeteor[i].radius; |
294 | if (mediumMeteor[i].position.y > screenHeight + mediumMeteor[i].radius) mediumMeteor[i].position.y = -(mediumMeteor[i].radius); |
295 | else if (mediumMeteor[i].position.y < 0 - mediumMeteor[i].radius) mediumMeteor[i].position.y = screenHeight + mediumMeteor[i].radius; |
296 | } |
297 | } |
298 | |
299 | for (int i = 0; i < MAX_SMALL_METEORS; i++) |
300 | { |
301 | if (smallMeteor[i].active) |
302 | { |
303 | // movement |
304 | smallMeteor[i].position.x += smallMeteor[i].speed.x; |
305 | smallMeteor[i].position.y += smallMeteor[i].speed.y; |
306 | |
307 | // wall behaviour |
308 | if (smallMeteor[i].position.x > screenWidth + smallMeteor[i].radius) smallMeteor[i].position.x = -(smallMeteor[i].radius); |
309 | else if (smallMeteor[i].position.x < 0 - smallMeteor[i].radius) smallMeteor[i].position.x = screenWidth + smallMeteor[i].radius; |
310 | if (smallMeteor[i].position.y > screenHeight + smallMeteor[i].radius) smallMeteor[i].position.y = -(smallMeteor[i].radius); |
311 | else if (smallMeteor[i].position.y < 0 - smallMeteor[i].radius) smallMeteor[i].position.y = screenHeight + smallMeteor[i].radius; |
312 | } |
313 | } |
314 | } |
315 | } |
316 | else |
317 | { |
318 | if (IsKeyPressed(KEY_ENTER)) |
319 | { |
320 | InitGame(); |
321 | gameOver = false; |
322 | } |
323 | } |
324 | } |
325 | |
326 | // Draw game (one frame) |
327 | void DrawGame(void) |
328 | { |
329 | BeginDrawing(); |
330 | |
331 | ClearBackground(RAYWHITE); |
332 | |
333 | if (!gameOver) |
334 | { |
335 | // Draw spaceship |
336 | Vector2 v1 = { player.position.x + sinf(player.rotation*DEG2RAD)*(shipHeight), player.position.y - cosf(player.rotation*DEG2RAD)*(shipHeight) }; |
337 | Vector2 v2 = { player.position.x - cosf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2), player.position.y - sinf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2) }; |
338 | Vector2 v3 = { player.position.x + cosf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2), player.position.y + sinf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2) }; |
339 | DrawTriangle(v1, v2, v3, MAROON); |
340 | |
341 | // Draw meteor |
342 | for (int i = 0;i < MAX_MEDIUM_METEORS; i++) |
343 | { |
344 | if (mediumMeteor[i].active) DrawCircleV(mediumMeteor[i].position, mediumMeteor[i].radius, GRAY); |
345 | else DrawCircleV(mediumMeteor[i].position, mediumMeteor[i].radius, Fade(LIGHTGRAY, 0.3f)); |
346 | } |
347 | |
348 | for (int i = 0;i < MAX_SMALL_METEORS; i++) |
349 | { |
350 | if (smallMeteor[i].active) DrawCircleV(smallMeteor[i].position, smallMeteor[i].radius, DARKGRAY); |
351 | else DrawCircleV(smallMeteor[i].position, smallMeteor[i].radius, Fade(LIGHTGRAY, 0.3f)); |
352 | } |
353 | |
354 | DrawText(TextFormat("TIME: %.02f" , (float)framesCounter/60), 10, 10, 20, BLACK); |
355 | |
356 | if (pause) DrawText("GAME PAUSED" , screenWidth/2 - MeasureText("GAME PAUSED" , 40)/2, screenHeight/2 - 40, 40, GRAY); |
357 | } |
358 | else DrawText("PRESS [ENTER] TO PLAY AGAIN" , GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN" , 20)/2, GetScreenHeight()/2 - 50, 20, GRAY); |
359 | |
360 | EndDrawing(); |
361 | //---------------------------------------------------------------------------------- |
362 | } |
363 | |
364 | // Unload game variables |
365 | void UnloadGame(void) |
366 | { |
367 | // TODO: Unload all dynamic loaded data (textures, sounds, models...) |
368 | } |
369 | |
370 | // Update and Draw (one frame) |
371 | void UpdateDrawFrame(void) |
372 | { |
373 | UpdateGame(); |
374 | DrawGame(); |
375 | } |
376 | |