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//----------------------------------------------------------------------------------
36typedef struct Player {
37 Vector2 position;
38 Vector2 speed;
39 float acceleration;
40 float rotation;
41 Vector3 collider;
42 Color color;
43} Player;
44
45typedef 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//------------------------------------------------------------------------------------
56static const int screenWidth = 800;
57static const int screenHeight = 450;
58
59static int framesCounter = 0;
60static bool gameOver = false;
61static bool pause = false;
62
63// NOTE: Defined triangle is isosceles with common angles of 70 degrees.
64static float shipHeight = 0.0f;
65
66static Player player = { 0 };
67static Meteor mediumMeteor[MAX_MEDIUM_METEORS] = { 0 };
68static Meteor smallMeteor[MAX_SMALL_METEORS] = { 0 };
69
70//------------------------------------------------------------------------------------
71// Module Functions Declaration (local)
72//------------------------------------------------------------------------------------
73static void InitGame(void); // Initialize game
74static void UpdateGame(void); // Update game (one frame)
75static void DrawGame(void); // Draw game (one frame)
76static void UnloadGame(void); // Unload game
77static void UpdateDrawFrame(void); // Update and Draw (one frame)
78
79//------------------------------------------------------------------------------------
80// Program main entry point
81//------------------------------------------------------------------------------------
82int 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
120void 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)
222void 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)
327void 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
365void UnloadGame(void)
366{
367 // TODO: Unload all dynamic loaded data (textures, sounds, models...)
368}
369
370// Update and Draw (one frame)
371void UpdateDrawFrame(void)
372{
373 UpdateGame();
374 DrawGame();
375}
376