1 | /******************************************************************************************* |
2 | * |
3 | * raylib - sample game: arkanoid |
4 | * |
5 | * Sample game Marc Palau 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 <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <time.h> |
19 | #include <math.h> |
20 | |
21 | #if defined(PLATFORM_WEB) |
22 | #include <emscripten/emscripten.h> |
23 | #endif |
24 | |
25 | //---------------------------------------------------------------------------------- |
26 | // Some Defines |
27 | //---------------------------------------------------------------------------------- |
28 | #define PLAYER_MAX_LIFE 5 |
29 | #define LINES_OF_BRICKS 5 |
30 | #define BRICKS_PER_LINE 20 |
31 | |
32 | //---------------------------------------------------------------------------------- |
33 | // Types and Structures Definition |
34 | //---------------------------------------------------------------------------------- |
35 | typedef enum GameScreen { LOGO, TITLE, GAMEPLAY, ENDING } GameScreen; |
36 | |
37 | typedef struct Player { |
38 | Vector2 position; |
39 | Vector2 size; |
40 | int life; |
41 | } Player; |
42 | |
43 | typedef struct Ball { |
44 | Vector2 position; |
45 | Vector2 speed; |
46 | int radius; |
47 | bool active; |
48 | } Ball; |
49 | |
50 | typedef struct Brick { |
51 | Vector2 position; |
52 | bool active; |
53 | } Brick; |
54 | |
55 | //------------------------------------------------------------------------------------ |
56 | // Global Variables Declaration |
57 | //------------------------------------------------------------------------------------ |
58 | static const int screenWidth = 800; |
59 | static const int screenHeight = 450; |
60 | |
61 | static bool gameOver = false; |
62 | static bool pause = false; |
63 | |
64 | static Player player = { 0 }; |
65 | static Ball ball = { 0 }; |
66 | static Brick brick[LINES_OF_BRICKS][BRICKS_PER_LINE] = { 0 }; |
67 | static Vector2 brickSize = { 0 }; |
68 | |
69 | //------------------------------------------------------------------------------------ |
70 | // Module Functions Declaration (local) |
71 | //------------------------------------------------------------------------------------ |
72 | static void InitGame(void); // Initialize game |
73 | static void UpdateGame(void); // Update game (one frame) |
74 | static void DrawGame(void); // Draw game (one frame) |
75 | static void UnloadGame(void); // Unload game |
76 | static void UpdateDrawFrame(void); // Update and Draw (one frame) |
77 | |
78 | //------------------------------------------------------------------------------------ |
79 | // Program main entry point |
80 | //------------------------------------------------------------------------------------ |
81 | int main(void) |
82 | { |
83 | // Initialization (Note windowTitle is unused on Android) |
84 | //--------------------------------------------------------- |
85 | InitWindow(screenWidth, screenHeight, "sample game: arkanoid" ); |
86 | |
87 | InitGame(); |
88 | |
89 | #if defined(PLATFORM_WEB) |
90 | emscripten_set_main_loop(UpdateDrawFrame, 0, 1); |
91 | #else |
92 | SetTargetFPS(60); |
93 | //-------------------------------------------------------------------------------------- |
94 | |
95 | // Main game loop |
96 | while (!WindowShouldClose()) // Detect window close button or ESC key |
97 | { |
98 | // Update and Draw |
99 | //---------------------------------------------------------------------------------- |
100 | UpdateDrawFrame(); |
101 | //---------------------------------------------------------------------------------- |
102 | } |
103 | #endif |
104 | // De-Initialization |
105 | //-------------------------------------------------------------------------------------- |
106 | UnloadGame(); // Unload loaded data (textures, sounds, models...) |
107 | |
108 | CloseWindow(); // Close window and OpenGL context |
109 | //-------------------------------------------------------------------------------------- |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | //------------------------------------------------------------------------------------ |
115 | // Module Functions Definitions (local) |
116 | //------------------------------------------------------------------------------------ |
117 | |
118 | // Initialize game variables |
119 | void InitGame(void) |
120 | { |
121 | brickSize = (Vector2){ GetScreenWidth()/BRICKS_PER_LINE, 40 }; |
122 | |
123 | // Initialize player |
124 | player.position = (Vector2){ screenWidth/2, screenHeight*7/8 }; |
125 | player.size = (Vector2){ screenWidth/10, 20 }; |
126 | player.life = PLAYER_MAX_LIFE; |
127 | |
128 | // Initialize ball |
129 | ball.position = (Vector2){ screenWidth/2, screenHeight*7/8 - 30 }; |
130 | ball.speed = (Vector2){ 0, 0 }; |
131 | ball.radius = 7; |
132 | ball.active = false; |
133 | |
134 | // Initialize bricks |
135 | int initialDownPosition = 50; |
136 | |
137 | for (int i = 0; i < LINES_OF_BRICKS; i++) |
138 | { |
139 | for (int j = 0; j < BRICKS_PER_LINE; j++) |
140 | { |
141 | brick[i][j].position = (Vector2){ j*brickSize.x + brickSize.x/2, i*brickSize.y + initialDownPosition }; |
142 | brick[i][j].active = true; |
143 | } |
144 | } |
145 | } |
146 | |
147 | // Update game (one frame) |
148 | void UpdateGame(void) |
149 | { |
150 | if (!gameOver) |
151 | { |
152 | if (IsKeyPressed('P')) pause = !pause; |
153 | |
154 | if (!pause) |
155 | { |
156 | // Player movement logic |
157 | if (IsKeyDown(KEY_LEFT)) player.position.x -= 5; |
158 | if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2; |
159 | if (IsKeyDown(KEY_RIGHT)) player.position.x += 5; |
160 | if ((player.position.x + player.size.x/2) >= screenWidth) player.position.x = screenWidth - player.size.x/2; |
161 | |
162 | // Ball launching logic |
163 | if (!ball.active) |
164 | { |
165 | if (IsKeyPressed(KEY_SPACE)) |
166 | { |
167 | ball.active = true; |
168 | ball.speed = (Vector2){ 0, -5 }; |
169 | } |
170 | } |
171 | |
172 | // Ball movement logic |
173 | if (ball.active) |
174 | { |
175 | ball.position.x += ball.speed.x; |
176 | ball.position.y += ball.speed.y; |
177 | } |
178 | else |
179 | { |
180 | ball.position = (Vector2){ player.position.x, screenHeight*7/8 - 30 }; |
181 | } |
182 | |
183 | // Collision logic: ball vs walls |
184 | if (((ball.position.x + ball.radius) >= screenWidth) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1; |
185 | if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1; |
186 | if ((ball.position.y + ball.radius) >= screenHeight) |
187 | { |
188 | ball.speed = (Vector2){ 0, 0 }; |
189 | ball.active = false; |
190 | |
191 | player.life--; |
192 | } |
193 | |
194 | // Collision logic: ball vs player |
195 | if (CheckCollisionCircleRec(ball.position, ball.radius, |
196 | (Rectangle){ player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y})) |
197 | { |
198 | if (ball.speed.y > 0) |
199 | { |
200 | ball.speed.y *= -1; |
201 | ball.speed.x = (ball.position.x - player.position.x)/(player.size.x/2)*5; |
202 | } |
203 | } |
204 | |
205 | // Collision logic: ball vs bricks |
206 | for (int i = 0; i < LINES_OF_BRICKS; i++) |
207 | { |
208 | for (int j = 0; j < BRICKS_PER_LINE; j++) |
209 | { |
210 | if (brick[i][j].active) |
211 | { |
212 | // Hit below |
213 | if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brickSize.y/2)) && |
214 | ((ball.position.y - ball.radius) > (brick[i][j].position.y + brickSize.y/2 + ball.speed.y)) && |
215 | ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y < 0)) |
216 | { |
217 | brick[i][j].active = false; |
218 | ball.speed.y *= -1; |
219 | } |
220 | // Hit above |
221 | else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brickSize.y/2)) && |
222 | ((ball.position.y + ball.radius) < (brick[i][j].position.y - brickSize.y/2 + ball.speed.y)) && |
223 | ((fabs(ball.position.x - brick[i][j].position.x)) < (brickSize.x/2 + ball.radius*2/3)) && (ball.speed.y > 0)) |
224 | { |
225 | brick[i][j].active = false; |
226 | ball.speed.y *= -1; |
227 | } |
228 | // Hit left |
229 | else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brickSize.x/2)) && |
230 | ((ball.position.x + ball.radius) < (brick[i][j].position.x - brickSize.x/2 + ball.speed.x)) && |
231 | ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x > 0)) |
232 | { |
233 | brick[i][j].active = false; |
234 | ball.speed.x *= -1; |
235 | } |
236 | // Hit right |
237 | else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brickSize.x/2)) && |
238 | ((ball.position.x - ball.radius) > (brick[i][j].position.x + brickSize.x/2 + ball.speed.x)) && |
239 | ((fabs(ball.position.y - brick[i][j].position.y)) < (brickSize.y/2 + ball.radius*2/3)) && (ball.speed.x < 0)) |
240 | { |
241 | brick[i][j].active = false; |
242 | ball.speed.x *= -1; |
243 | } |
244 | } |
245 | } |
246 | } |
247 | |
248 | // Game over logic |
249 | if (player.life <= 0) gameOver = true; |
250 | else |
251 | { |
252 | gameOver = true; |
253 | |
254 | for (int i = 0; i < LINES_OF_BRICKS; i++) |
255 | { |
256 | for (int j = 0; j < BRICKS_PER_LINE; j++) |
257 | { |
258 | if (brick[i][j].active) gameOver = false; |
259 | } |
260 | } |
261 | } |
262 | } |
263 | } |
264 | else |
265 | { |
266 | if (IsKeyPressed(KEY_ENTER)) |
267 | { |
268 | InitGame(); |
269 | gameOver = false; |
270 | } |
271 | } |
272 | } |
273 | |
274 | // Draw game (one frame) |
275 | void DrawGame(void) |
276 | { |
277 | BeginDrawing(); |
278 | |
279 | ClearBackground(RAYWHITE); |
280 | |
281 | if (!gameOver) |
282 | { |
283 | // Draw player bar |
284 | DrawRectangle(player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y, BLACK); |
285 | |
286 | // Draw player lives |
287 | for (int i = 0; i < player.life; i++) DrawRectangle(20 + 40*i, screenHeight - 30, 35, 10, LIGHTGRAY); |
288 | |
289 | // Draw ball |
290 | DrawCircleV(ball.position, ball.radius, MAROON); |
291 | |
292 | // Draw bricks |
293 | for (int i = 0; i < LINES_OF_BRICKS; i++) |
294 | { |
295 | for (int j = 0; j < BRICKS_PER_LINE; j++) |
296 | { |
297 | if (brick[i][j].active) |
298 | { |
299 | if ((i + j) % 2 == 0) DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, GRAY); |
300 | else DrawRectangle(brick[i][j].position.x - brickSize.x/2, brick[i][j].position.y - brickSize.y/2, brickSize.x, brickSize.y, DARKGRAY); |
301 | } |
302 | } |
303 | } |
304 | |
305 | if (pause) DrawText("GAME PAUSED" , screenWidth/2 - MeasureText("GAME PAUSED" , 40)/2, screenHeight/2 - 40, 40, GRAY); |
306 | } |
307 | else DrawText("PRESS [ENTER] TO PLAY AGAIN" , GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN" , 20)/2, GetScreenHeight()/2 - 50, 20, GRAY); |
308 | |
309 | EndDrawing(); |
310 | } |
311 | |
312 | // Unload game variables |
313 | void UnloadGame(void) |
314 | { |
315 | // TODO: Unload all dynamic loaded data (textures, sounds, models...) |
316 | } |
317 | |
318 | // Update and Draw (one frame) |
319 | void UpdateDrawFrame(void) |
320 | { |
321 | UpdateGame(); |
322 | DrawGame(); |
323 | } |
324 | |