1 | /******************************************************************************************* |
2 | * |
3 | * raylib - sample game: snake |
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 | #if defined(PLATFORM_WEB) |
17 | #include <emscripten/emscripten.h> |
18 | #endif |
19 | |
20 | //---------------------------------------------------------------------------------- |
21 | // Some Defines |
22 | //---------------------------------------------------------------------------------- |
23 | #define SNAKE_LENGTH 256 |
24 | #define SQUARE_SIZE 31 |
25 | |
26 | //---------------------------------------------------------------------------------- |
27 | // Types and Structures Definition |
28 | //---------------------------------------------------------------------------------- |
29 | typedef struct Snake { |
30 | Vector2 position; |
31 | Vector2 size; |
32 | Vector2 speed; |
33 | Color color; |
34 | } Snake; |
35 | |
36 | typedef struct Food { |
37 | Vector2 position; |
38 | Vector2 size; |
39 | bool active; |
40 | Color color; |
41 | } Food; |
42 | |
43 | //------------------------------------------------------------------------------------ |
44 | // Global Variables Declaration |
45 | //------------------------------------------------------------------------------------ |
46 | static const int screenWidth = 800; |
47 | static const int screenHeight = 450; |
48 | |
49 | static int framesCounter = 0; |
50 | static bool gameOver = false; |
51 | static bool pause = false; |
52 | |
53 | static Food fruit = { 0 }; |
54 | static Snake snake[SNAKE_LENGTH] = { 0 }; |
55 | static Vector2 snakePosition[SNAKE_LENGTH] = { 0 }; |
56 | static bool allowMove = false; |
57 | static Vector2 offset = { 0 }; |
58 | static int counterTail = 0; |
59 | |
60 | //------------------------------------------------------------------------------------ |
61 | // Module Functions Declaration (local) |
62 | //------------------------------------------------------------------------------------ |
63 | static void InitGame(void); // Initialize game |
64 | static void UpdateGame(void); // Update game (one frame) |
65 | static void DrawGame(void); // Draw game (one frame) |
66 | static void UnloadGame(void); // Unload game |
67 | static void UpdateDrawFrame(void); // Update and Draw (one frame) |
68 | |
69 | //------------------------------------------------------------------------------------ |
70 | // Program main entry point |
71 | //------------------------------------------------------------------------------------ |
72 | int main(void) |
73 | { |
74 | // Initialization (Note windowTitle is unused on Android) |
75 | //--------------------------------------------------------- |
76 | InitWindow(screenWidth, screenHeight, "sample game: snake" ); |
77 | |
78 | InitGame(); |
79 | |
80 | #if defined(PLATFORM_WEB) |
81 | emscripten_set_main_loop(UpdateDrawFrame, 0, 1); |
82 | #else |
83 | SetTargetFPS(60); |
84 | //-------------------------------------------------------------------------------------- |
85 | |
86 | // Main game loop |
87 | while (!WindowShouldClose()) // Detect window close button or ESC key |
88 | { |
89 | // Update and Draw |
90 | //---------------------------------------------------------------------------------- |
91 | UpdateDrawFrame(); |
92 | //---------------------------------------------------------------------------------- |
93 | } |
94 | #endif |
95 | // De-Initialization |
96 | //-------------------------------------------------------------------------------------- |
97 | UnloadGame(); // Unload loaded data (textures, sounds, models...) |
98 | |
99 | CloseWindow(); // Close window and OpenGL context |
100 | //-------------------------------------------------------------------------------------- |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | //------------------------------------------------------------------------------------ |
106 | // Module Functions Definitions (local) |
107 | //------------------------------------------------------------------------------------ |
108 | |
109 | // Initialize game variables |
110 | void InitGame(void) |
111 | { |
112 | framesCounter = 0; |
113 | gameOver = false; |
114 | pause = false; |
115 | |
116 | counterTail = 1; |
117 | allowMove = false; |
118 | |
119 | offset.x = screenWidth%SQUARE_SIZE; |
120 | offset.y = screenHeight%SQUARE_SIZE; |
121 | |
122 | for (int i = 0; i < SNAKE_LENGTH; i++) |
123 | { |
124 | snake[i].position = (Vector2){ offset.x/2, offset.y/2 }; |
125 | snake[i].size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE }; |
126 | snake[i].speed = (Vector2){ SQUARE_SIZE, 0 }; |
127 | |
128 | if (i == 0) snake[i].color = DARKBLUE; |
129 | else snake[i].color = BLUE; |
130 | } |
131 | |
132 | for (int i = 0; i < SNAKE_LENGTH; i++) |
133 | { |
134 | snakePosition[i] = (Vector2){ 0.0f, 0.0f }; |
135 | } |
136 | |
137 | fruit.size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE }; |
138 | fruit.color = SKYBLUE; |
139 | fruit.active = false; |
140 | } |
141 | |
142 | // Update game (one frame) |
143 | void UpdateGame(void) |
144 | { |
145 | if (!gameOver) |
146 | { |
147 | if (IsKeyPressed('P')) pause = !pause; |
148 | |
149 | if (!pause) |
150 | { |
151 | // Player control |
152 | if (IsKeyPressed(KEY_RIGHT) && (snake[0].speed.x == 0) && allowMove) |
153 | { |
154 | snake[0].speed = (Vector2){ SQUARE_SIZE, 0 }; |
155 | allowMove = false; |
156 | } |
157 | if (IsKeyPressed(KEY_LEFT) && (snake[0].speed.x == 0) && allowMove) |
158 | { |
159 | snake[0].speed = (Vector2){ -SQUARE_SIZE, 0 }; |
160 | allowMove = false; |
161 | } |
162 | if (IsKeyPressed(KEY_UP) && (snake[0].speed.y == 0) && allowMove) |
163 | { |
164 | snake[0].speed = (Vector2){ 0, -SQUARE_SIZE }; |
165 | allowMove = false; |
166 | } |
167 | if (IsKeyPressed(KEY_DOWN) && (snake[0].speed.y == 0) && allowMove) |
168 | { |
169 | snake[0].speed = (Vector2){ 0, SQUARE_SIZE }; |
170 | allowMove = false; |
171 | } |
172 | |
173 | // Snake movement |
174 | for (int i = 0; i < counterTail; i++) snakePosition[i] = snake[i].position; |
175 | |
176 | if ((framesCounter%5) == 0) |
177 | { |
178 | for (int i = 0; i < counterTail; i++) |
179 | { |
180 | if (i == 0) |
181 | { |
182 | snake[0].position.x += snake[0].speed.x; |
183 | snake[0].position.y += snake[0].speed.y; |
184 | allowMove = true; |
185 | } |
186 | else snake[i].position = snakePosition[i-1]; |
187 | } |
188 | } |
189 | |
190 | // Wall behaviour |
191 | if (((snake[0].position.x) > (screenWidth - offset.x)) || |
192 | ((snake[0].position.y) > (screenHeight - offset.y)) || |
193 | (snake[0].position.x < 0) || (snake[0].position.y < 0)) |
194 | { |
195 | gameOver = true; |
196 | } |
197 | |
198 | // Collision with yourself |
199 | for (int i = 1; i < counterTail; i++) |
200 | { |
201 | if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) gameOver = true; |
202 | } |
203 | |
204 | // Fruit position calculation |
205 | if (!fruit.active) |
206 | { |
207 | fruit.active = true; |
208 | fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 }; |
209 | |
210 | for (int i = 0; i < counterTail; i++) |
211 | { |
212 | while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y)) |
213 | { |
214 | fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 }; |
215 | i = 0; |
216 | } |
217 | } |
218 | } |
219 | |
220 | // Collision |
221 | if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) && |
222 | (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y)) |
223 | { |
224 | snake[counterTail].position = snakePosition[counterTail - 1]; |
225 | counterTail += 1; |
226 | fruit.active = false; |
227 | } |
228 | |
229 | framesCounter++; |
230 | } |
231 | } |
232 | else |
233 | { |
234 | if (IsKeyPressed(KEY_ENTER)) |
235 | { |
236 | InitGame(); |
237 | gameOver = false; |
238 | } |
239 | } |
240 | } |
241 | |
242 | // Draw game (one frame) |
243 | void DrawGame(void) |
244 | { |
245 | BeginDrawing(); |
246 | |
247 | ClearBackground(RAYWHITE); |
248 | |
249 | if (!gameOver) |
250 | { |
251 | // Draw grid lines |
252 | for (int i = 0; i < screenWidth/SQUARE_SIZE + 1; i++) |
253 | { |
254 | DrawLineV((Vector2){SQUARE_SIZE*i + offset.x/2, offset.y/2}, (Vector2){SQUARE_SIZE*i + offset.x/2, screenHeight - offset.y/2}, LIGHTGRAY); |
255 | } |
256 | |
257 | for (int i = 0; i < screenHeight/SQUARE_SIZE + 1; i++) |
258 | { |
259 | DrawLineV((Vector2){offset.x/2, SQUARE_SIZE*i + offset.y/2}, (Vector2){screenWidth - offset.x/2, SQUARE_SIZE*i + offset.y/2}, LIGHTGRAY); |
260 | } |
261 | |
262 | // Draw snake |
263 | for (int i = 0; i < counterTail; i++) DrawRectangleV(snake[i].position, snake[i].size, snake[i].color); |
264 | |
265 | // Draw fruit to pick |
266 | DrawRectangleV(fruit.position, fruit.size, fruit.color); |
267 | |
268 | if (pause) DrawText("GAME PAUSED" , screenWidth/2 - MeasureText("GAME PAUSED" , 40)/2, screenHeight/2 - 40, 40, GRAY); |
269 | } |
270 | else DrawText("PRESS [ENTER] TO PLAY AGAIN" , GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN" , 20)/2, GetScreenHeight()/2 - 50, 20, GRAY); |
271 | |
272 | EndDrawing(); |
273 | } |
274 | |
275 | // Unload game variables |
276 | void UnloadGame(void) |
277 | { |
278 | // TODO: Unload all dynamic loaded data (textures, sounds, models...) |
279 | } |
280 | |
281 | // Update and Draw (one frame) |
282 | void UpdateDrawFrame(void) |
283 | { |
284 | UpdateGame(); |
285 | DrawGame(); |
286 | } |
287 | |