/*******************************************************************************************
|
|
*
|
|
* raylib - sample game: snake
|
|
*
|
|
* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria
|
|
*
|
|
* This game has been created using raylib v1.3 (www.raylib.com)
|
|
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
|
*
|
|
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
|
|
*
|
|
********************************************************************************************/
|
|
|
|
#include "raylib.h"
|
|
|
|
#if defined(PLATFORM_WEB)
|
|
#include <emscripten/emscripten.h>
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Some Defines
|
|
//----------------------------------------------------------------------------------
|
|
#define SNAKE_LENGTH 256
|
|
#define SQUARE_SIZE 31
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Types and Structures Definition
|
|
//----------------------------------------------------------------------------------
|
|
typedef struct Snake {
|
|
Vector2 position;
|
|
Vector2 size;
|
|
Vector2 speed;
|
|
Color color;
|
|
} Snake;
|
|
|
|
typedef struct Food {
|
|
Vector2 position;
|
|
Vector2 size;
|
|
bool active;
|
|
Color color;
|
|
} Food;
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Global Variables Declaration
|
|
//------------------------------------------------------------------------------------
|
|
static int screenWidth = 800;
|
|
static int screenHeight = 450;
|
|
|
|
static int framesCounter;
|
|
static bool gameOver;
|
|
static bool pause;
|
|
|
|
static Food fruit;
|
|
static Snake snake[SNAKE_LENGTH];
|
|
static Vector2 snakePosition[SNAKE_LENGTH];
|
|
static bool allowMove;
|
|
static Vector2 offset;
|
|
static int counterTail;
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Module Functions Declaration (local)
|
|
//------------------------------------------------------------------------------------
|
|
static void InitGame(void); // Initialize game
|
|
static void UpdateGame(void); // Update game (one frame)
|
|
static void DrawGame(void); // Draw game (one frame)
|
|
static void UnloadGame(void); // Unload game
|
|
static void UpdateDrawFrame(void); // Update and Draw (one frame)
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Program main entry point
|
|
//------------------------------------------------------------------------------------
|
|
int main()
|
|
{
|
|
// Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
InitWindow(screenWidth, screenHeight, "sample game: snake");
|
|
|
|
InitGame();
|
|
|
|
#if defined(PLATFORM_WEB)
|
|
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
|
|
#else
|
|
|
|
SetTargetFPS(60);
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
// Update
|
|
//----------------------------------------------------------------------------------
|
|
UpdateGame();
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Draw
|
|
//----------------------------------------------------------------------------------
|
|
DrawGame();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
#endif
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
UnloadGame(); // Unload loaded data (textures, sounds, models...)
|
|
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// Module Functions Definitions (local)
|
|
//------------------------------------------------------------------------------------
|
|
|
|
// Initialize game variables
|
|
void InitGame(void)
|
|
{
|
|
framesCounter = 0;
|
|
gameOver = false;
|
|
pause = false;
|
|
|
|
counterTail = 1;
|
|
allowMove = false;
|
|
|
|
offset.x = screenWidth%SQUARE_SIZE;
|
|
offset.y = screenHeight%SQUARE_SIZE;
|
|
|
|
for (int i = 0; i < SNAKE_LENGTH; i++)
|
|
{
|
|
snake[i].position = (Vector2){ offset.x/2, offset.y/2 };
|
|
snake[i].size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
|
|
snake[i].speed = (Vector2){ SQUARE_SIZE, 0 };
|
|
|
|
if (i == 0) snake[i].color = DARKBLUE;
|
|
else snake[i].color = BLUE;
|
|
}
|
|
|
|
for (int i = 0; i < SNAKE_LENGTH; i++)
|
|
{
|
|
snakePosition[i] = (Vector2){ 0.0f, 0.0f };
|
|
}
|
|
|
|
fruit.size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
|
|
fruit.color = SKYBLUE;
|
|
fruit.active = false;
|
|
}
|
|
|
|
// Update game (one frame)
|
|
void UpdateGame(void)
|
|
{
|
|
if (!gameOver)
|
|
{
|
|
if (IsKeyPressed('P')) pause = !pause;
|
|
|
|
if (!pause)
|
|
{
|
|
// control
|
|
if (IsKeyPressed(KEY_RIGHT) && (snake[0].speed.x == 0) && allowMove)
|
|
{
|
|
snake[0].speed = (Vector2){ SQUARE_SIZE, 0 };
|
|
allowMove = false;
|
|
}
|
|
if (IsKeyPressed(KEY_LEFT) && (snake[0].speed.x == 0) && allowMove)
|
|
{
|
|
snake[0].speed = (Vector2){ -SQUARE_SIZE, 0 };
|
|
allowMove = false;
|
|
}
|
|
if (IsKeyPressed(KEY_UP) && (snake[0].speed.y == 0) && allowMove)
|
|
{
|
|
snake[0].speed = (Vector2){ 0, -SQUARE_SIZE };
|
|
allowMove = false;
|
|
}
|
|
if (IsKeyPressed(KEY_DOWN) && (snake[0].speed.y == 0) && allowMove)
|
|
{
|
|
snake[0].speed = (Vector2){ 0, SQUARE_SIZE };
|
|
allowMove = false;
|
|
}
|
|
|
|
// movement
|
|
for (int i = 0; i < counterTail; i++) snakePosition[i] = snake[i].position;
|
|
|
|
if ((framesCounter%5) == 0)
|
|
{
|
|
for (int i = 0; i < counterTail; i++)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
snake[0].position.x += snake[0].speed.x;
|
|
snake[0].position.y += snake[0].speed.y;
|
|
allowMove = true;
|
|
}
|
|
else snake[i].position = snakePosition[i-1];
|
|
}
|
|
}
|
|
|
|
// wall behaviour
|
|
if (((snake[0].position.x) > (screenWidth - offset.x)) ||
|
|
((snake[0].position.y) > (screenHeight - offset.y)) ||
|
|
(snake[0].position.x < 0) || (snake[0].position.y < 0))
|
|
{
|
|
gameOver = true;
|
|
}
|
|
|
|
// collision with yourself
|
|
for (int i = 1; i < counterTail; i++)
|
|
{
|
|
if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) gameOver = true;
|
|
}
|
|
|
|
// TODO: review logic: fruit.position calculation
|
|
if (!fruit.active)
|
|
{
|
|
fruit.active = true;
|
|
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 };
|
|
|
|
for (int i = 0; i < counterTail; i++)
|
|
{
|
|
while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
|
|
{
|
|
fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE };
|
|
i = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// collision
|
|
if (CheckCollisionRecs((Rectangle){(int)snake[0].position.x, (int)snake[0].position.y, (int)snake[0].size.x, (int)snake[0].size.y},
|
|
(Rectangle){(int)fruit.position.x, (int)fruit.position.y, (int)fruit.size.x, (int)fruit.size.y}))
|
|
{
|
|
snake[counterTail].position = snakePosition[counterTail - 1];
|
|
counterTail += 1;
|
|
fruit.active = false;
|
|
}
|
|
|
|
framesCounter++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsKeyPressed(KEY_ENTER))
|
|
{
|
|
InitGame();
|
|
gameOver = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw game (one frame)
|
|
void DrawGame(void)
|
|
{
|
|
BeginDrawing();
|
|
|
|
ClearBackground(RAYWHITE);
|
|
|
|
if (!gameOver)
|
|
{
|
|
// Draw grid lines
|
|
for (int i = 0; i < screenWidth/SQUARE_SIZE + 1; i++)
|
|
{
|
|
DrawLineV((Vector2){SQUARE_SIZE*i + offset.x/2, offset.y/2}, (Vector2){SQUARE_SIZE*i + offset.x/2, screenHeight - offset.y/2}, LIGHTGRAY);
|
|
}
|
|
|
|
for (int i = 0; i < screenHeight/SQUARE_SIZE + 1; i++)
|
|
{
|
|
DrawLineV((Vector2){offset.x/2, SQUARE_SIZE*i + offset.y/2}, (Vector2){screenWidth - offset.x/2, SQUARE_SIZE*i + offset.y/2}, LIGHTGRAY);
|
|
}
|
|
|
|
// Draw snake
|
|
for (int i = 0; i < counterTail; i++) DrawRectangleV(snake[i].position, snake[i].size, snake[i].color);
|
|
|
|
// Draw fruit to pick
|
|
DrawRectangleV(fruit.position, fruit.size, fruit.color);
|
|
|
|
if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
|
|
}
|
|
else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
|
|
|
|
EndDrawing();
|
|
}
|
|
|
|
// Unload game variables
|
|
void UnloadGame(void)
|
|
{
|
|
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
|
|
}
|
|
|
|
// Update and Draw (one frame)
|
|
void UpdateDrawFrame(void)
|
|
{
|
|
UpdateGame();
|
|
DrawGame();
|
|
}
|