Snake
Snake
Snake
on
A simple Snake Game implemented
using the raylib library in C++
Submitted by
VANLALRINGA
Registration No: 1487335
NIELIT A Level
Date of Submission –
(VANLALRINGA)
NIELIT ‘A Level’
Regn. No.1487335
Table of Contents
ACKNOWLEDGEMENT
Chapter Title Page No.
1. INTRODUCTION 1
2. ABSTRACT 3
3. SYSTEM ANALYSIS AND DESIGN 4
a) Requirement Gathering
b) Feasibility Study
c) Architecture Overview
d) Structural Design
e) User Interface Design
f) Data Design
g) Implementation Strategy
4. SYSTEM TESTING 8
a) Testing Objectives
b) Testing Types
c) System Testing Scenarios
d) Testing Approach
e) Test Environment
f) Testing Documentation
5. RAYLIB GRAPHICS LIBRARY 11
6. GAME INSTRUCTION 13
7. GAME IMPLEMENTATION 14
a) Creating Game Loop
b) The Food Creation Process
c) Creating the Snake
d) Moving the Snake
e) Making the Snake Eat the Food
f) Expanding the Snake’s Length
g) Ensuring Collision Check’s and Game Over
Conditions
h) Enhancing Game Visuals
i) Adding Score
j) Adding Sound
8. LIMITATIONS OF THE PROJECT 40
9. CONCLUSION 42
10. SOURCE CODE LISTING 43
11. REFERENCES 51
Snake
INTRODUCTION
Overview
The Snake game project involves developing a classic game where players
control a snake moving around a grid, eating food, and growing in length. The
game ends when the snake collides with itself or the boundaries. It's
implemented using the Raylib library in C++ for graphics and user interaction.
Objective
The primary objective of this project is to create an interactive and enjoyable
gaming experience by implementing a classic Snake game. This involves
developing gameplay mechanics that allow the player to control the snake, eat
food, avoid collisions, and track progress through scoring.
1
Snake
Scope
• Basic Gameplay: Implement the core mechanics of controlling the snake's
movement using arrow keys and growing in length upon eating food.
• Collision Detection: Handle collisions with the snake's own body and the
game boundaries (walls).
• User Interface: Display the game window, the snake, food, and necessary
game-related information (score, etc.).
• Game Loop: Implement a continuous game loop for smooth gameplay and
responsiveness.
• Simple Graphics: Use basic shapes or sprites to represent the snake, food,
and other game elements.
Aim
• Educational Purpose: Serve as a beginner-friendly project to understand
game development basics using the Raylib library and C++.
• Fundamentals of Game Logic: Implement core gaming concepts such as
user input handling, collision detection, game state management, and
rendering.
• Experience with Raylib: Gain practical experience in using Raylib for
graphics rendering and user interaction.
• Foundation for Expansion: Create a basic working model that can be
expanded upon to add additional features, better graphics, sound effects,
and increased complexity.
In conclusion, the Snake game project using Raylib in C++ aims to provide an
entry-level experience in game development by creating a classic game. It
focuses on fundamental game development concepts and serves as a starting
point for individuals interested in learning game programming using the
Raylib library and C++.
2
Snake
ABSTRACT
This project involves the development of a classic Snake game using the Raylib
library in C++. The game provides an introductory platform for individuals
interested in learning game development concepts and utilizing graphics
libraries.
The primary goal of the project is to create an interactive and functional Snake
game that showcases fundamental game design principles. The game allows
players to control a snake moving within a grid, eating food to grow while
avoiding collisions with itself and the game boundaries.
Implemented functionalities include user input handling for snake movement,
collision detection, rendering game elements using Raylib's graphics functions,
and basic game state management.
While the game remains basic in its features and presentation, it serves as an
educational tool, providing insights into game development workflows,
modular design, graphics rendering, and user interaction using the Raylib
library. Additionally, it introduces developers to the iterative process of game
development, laying a foundation for potential enhancements and future
projects.
This project acts as a starting point for novice game developers to grasp
fundamental concepts, gain practical experience, and potentially expand upon
the game's features, graphics, and gameplay for a more comprehensive and
engaging gaming experience.
3
Snake
Functional Requirements
• User Control: The game must allow users to control the snake's
movement using arrow keys or directional controls.
• Snake Growth: Upon consuming food, the snake's length should
increase, simulating growth.
• Collision Detection: Implement collision detection mechanisms to
identify instances where the snake collides with itself or the game
boundaries, triggering game-over conditions.
• Game Display: The game window should render the game grid, the
snake, food items, score, and other essential game information.
Non-Functional Requirements
Feasibility Study
Objective: The feasibility study evaluates the technical and practical feasibility
of developing the Snake game using the Raylib library in C++.
4
Snake
Technical Feasibility
Practical Feasibility
Architecture Overview
Component Overview
• Main Loop: Responsible for managing the game flow, updating game
state, and rendering game elements.
• User Input Handling: Captures and processes user input for controlling
the snake's movement.
• Game Logic: Implements the rules, collision detection, and game state
management.
• Graphics Rendering: Utilizes Raylib's functions to render game elements
such as the snake, food items, and the game grid.
• Audio Handling: Manages sound effects and background audio using
Raylib's audio functionalities.
Structural Design
Objective: The structural design focuses on breaking down the system into
individual modules and components.
5
Snake
Module Breakdown
• Main Module: Controls the game loop, initialization, and overall game
state management.
• Snake Module: Manages the snake's movement, growth, collision
detection, and interactions with other game elements.
• User Interface Module: Handles rendering of game elements, UI
components, and user interaction controls.
• Input Module: Captures and processes user input for controlling the
snake's direction.
• Collision Detection Module: Detects collisions between different game
entities and triggers appropriate actions.
Objective: The user interface design specifies the visual elements and user
interaction mechanisms within the game.
• Game Display: Displays the game area, including the grid, snake, food
items, and essential game information like the score.
• Visual Elements: Utilizes Raylib's drawing functions to render game
elements with appropriate colours, shapes, and sizes for easy visibility
and distinction.
• User Interaction: Responds to user input from arrow keys or directional
controls, providing smooth and intuitive snake movement.
Data Design
Objective: The data design outlines the data structures and variables used within
the Snake game.
Data Structures
• Deque: Maintains the segments of the snake's body, allowing for easy
addition and removal of segments during growth or collision.
6
Snake
Implementation Strategy
Step-by-Step Development
• Setup: Initialize the game window, set up necessary libraries, and
establish the basic game environment.
• Logic Implementation: Implement snake movement, collision detection,
growth mechanics, and game rules.
• User Interface Enhancement: Improve visual elements, text rendering, and
user interaction for a polished interface.
• Testing and Refinement: Conduct comprehensive testing, including
functional testing, user testing, and debugging to refine the game's
features and ensure optimal performance.
7
Snake
SYSTEM TESTING
Testing Objectives
Objective: The primary objective of system testing for the Snake game is to
ensure its functionality, usability, performance, and compatibility with
specified requirements.
Detailed Explanation
• Functionality: Verify that all game functionalities, including snake
movement, collision detection, scoring, and game-over conditions, work
as intended without errors or glitches.
• Usability: Evaluate the user interface and interaction, ensuring intuitive
controls, clear visual elements, and a user-friendly experience.
• Performance: Test the game for smooth gameplay, responsive controls,
and adequate performance across various hardware configurations.
• Compatibility: Ensure the game runs seamlessly on different operating
systems (Windows, Linux, macOS) without any platform-specific issues.
Testing Types
Detailed Explanation
Objective: To define specific scenarios to test various aspects of the Snake game
functionality.
8
Snake
Detailed Explanation
• Snake Movement: Test arrow key inputs to confirm that the snake moves
correctly in the specified direction within the game grid.
• Collision Detection: Evaluate scenarios where the snake collides with itself
or the game boundaries to trigger the appropriate game-over conditions.
• Eating Food and Growth: Test scenarios where the snake consumes food,
ensuring proper growth and regeneration of food at different locations.
• User Interface: Validate the display of game elements, scores, and user
interaction within the game window.
Testing Approach
Detailed Explanation
Test Environment
Detailed Explanation
9
Snake
Testing Documentation
Detailed Explanation
These comprehensive system testing notes provide detailed insights into the
objectives, types, scenarios, approach, test environment, and documentation
processes involved in testing the Snake game, ensuring a thorough evaluation
and validation of its functionalities and performance.
10
Snake
11
Snake
The seven modules share a common header, named raylib.h. All API
(application programming interface) are defined inside that header file, despite
being internally divided into seven modules. The reason being that users only
needs to include raylib.h to access all of the raylib functionality. Other libraries
often use a header for every module (so users can select the ones they include),
but this complicates the dependencies. The simple approach that raylib adopts
is just easier for novice (like me) users.
Beyond the seven main modules that are described above, there is a small
collection of additional modules that implement extra features:
• raymath: Vector2, Vector3, Matrix and Quaternion math related functions.
• rcamera: 3D Camera system (free, 1st person, 3rd person, custom).
• rgestures: Touch gesture detection and processing (Tap, Swipe, Drag,
Pinch).
• raygui: Simple IMGUI system with several controls for tools development.
• rres: Resource packaging file-format with some tools provided.
raylib extra modules are designed to be as decoupled as possible from the other
modules.
12
Snake
GAME INSTRUCTIONS
Objective
Guide the snake to eat the food pellets scattered across the play area, growing
longer with each pellet consumed. Avoid collisions with walls or the snake's
own body to prevent game over.
Controls
Gameplay
• The game begins with a single snake segment on the play area.
• Direct the snake using the arrow keys to navigate and consume food
pellets.
• The snake will grow longer with each pellet eaten.
• Colliding with the game window's borders or the snake's own body will
end the game.
• The game restarts when a collision occurs. Press any arrow key to restart.
Scoring
Audio
Enjoy sounds each time the snake eats food or collides with the walls.
Objective
Enjoy the retro gaming experience and test your agility and strategy as you
control the snake, aiming to achieve the highest score!
13
Snake
GAME IMPLEMENTATION
The initial step in constructing our Snake game involves establishing a game
loop, which constitutes the heartbeat of the game, persistently running until its
closure. The game loop serves as a core mechanism for updating object
positions and collision detection, making movements appear seamless.
To initiate this, we start by setting up a blank screen and the game loop. Begin
by accessing the C++ Starter Template for Raylib via VS code or downloading
the template project from the provided GitHub repository. After removing the
content within the main.cpp file, a test message, "Starting the game," is printed
to verify the setup.
14
Snake
The game loop encompasses three primary phases: event handling, position
updates, and object rendering. The loop operates within a while() loop,
continuously executing until the game concludes. Event handling includes
functions like WindowShouldClose() to detect termination signals, while
BeginDrawing() and EndDrawing() control the canvas and graphics rendering,
respectively.
Setting the frame rate via SetTargetFPS() at 60 frames per second ensures
consistent game speed across various systems. Without defining the frame rate,
the game's speed might vary based on the computer's capabilities.
15
Snake
Our Colors
This basic setup concludes the game loop establishment, signifying progress in
our project's development.
16
Snake
To facilitate the creation of the food object, a fundamental game logic strategy
is employed. Utilizing a widely adopted game development technique, a grid
system is established to aid in object positioning and movement within the
game display. Although invisible in practice, this grid substantially facilitates
the object's positioning.
17
Snake
The subsequent step involves the creation of the Food class to represent the
food object in the game. This class is designed to maintain crucial attributes,
primarily focusing on the object's position within the grid. Leveraging the
Vector2 struct provided by the Raylib library, the position attribute of the Food
class holds the x and y coordinates of the food object within the grid.
Accessing x and y
position.x
position.y
Vector2 {5, 6}
Implementing a draw() method within the Food class is crucial for rendering
the food object on the screen. Utilizing Raylib's graphics functions, specifically
the DrawRectangle() method, a dark green square representing the food object
is drawn on the screen based on its position within the grid.
18
Snake
Initiating a Food object outside the game loop and invoking its draw() method
within the loop results in the appearance of a dark green square, symbolizing
the food object within the game.
Transitioning from drawing basic shapes to utilizing an actual image for the
food object requires the incorporation of an image file. To achieve this, the
LoadImage() function is utilized to load the image file named "food.png" located
in the "Graphics" folder of the game directory. This image is loaded into an
Image data structure, serving as a pixel data container for graphical imagery.
The draw() method within the Food class is updated, replacing the previous
DrawRectangle() function with the DrawTexture() function to display the image
of the food object on the screen. By passing the texture, x and y coordinates, and
a tint color argument, the food image is drawn at the specified position within
the grid, enhancing the game's visual appeal.
19
Snake
Ensuring randomness in the food object's initial position is vital for gameplay
variety. Introducing a generate_random_pos() method within the Food class
utilizing GetRandomValue(), random x and y coordinates within the grid's range
are determined, enabling the food object to spawn in different positions upon
each game initiation.
20
Snake
Creating a Snake class, the body of the snake is defined as a deque containing
three Vector2 structs. These structs encapsulate the x and y coordinates for each
cell, symbolizing the snake's segments. Initialization of the deque involves
inputting coordinates, assigning 6,9 to the head and 5,9 as well as 4,9 to the tail
segments.
21
Snake
deque
A draw() method within the Snake class is formulated to render the snake body
on the screen. Utilizing a for loop iterating through each segment of the snake's
body, a dark green square, representing a cell, is drawn. Employing the deque's
size() method ensures the iteration through all the snake segments for accurate
rendering.
The creation of a Snake object outside the game loop, followed by the invocation
of its draw() method within the loop, results in the visible appearance of the
snake on the screen. Although initially rectangular in shape, the snake is
rendered as a collection of squares due to the drawn segments.
22
Snake
23
Snake
Given that Vector2 structs represent the snake's body segments, manipulating
their coordinates is a straightforward operation. Mathematical operations
performed with Vector2 structs facilitate calculating coordinates for the added
segment, simplifying the movement process. For instance, Vector2(1, 0) added
to the snake's head at (6, 9) results in (7, 9), symbolizing rightward movement.
Conversely, Vector2(-1, 0) prompts leftward movement. Similar logic applies to
Vector2(0, -1) and Vector2(0, 1), representing upward and downward
movements, respectively. Leveraging these vectors makes snake movement
more manageable and intuitive.
Initially, the snake moves swiftly, exceeding the game window boundaries due
to the update() method's frequent invocation within the game loop. To rectify
24
Snake
25
Snake
26
Snake
The scenario where the snake consumes the food occurs when the snake's head
occupies the same cell as the food. To implement this, a method named
CheckCollisionWithFood() is introduced within the Game class. This method is
responsible for verifying if the snake's head aligns with the food's cell during
every game update.
Utilizing the Vector2Equals() function, the condition to validate the head of the
snake aligning with the food's position is checked. Once this collision is
detected, a message, such as "Eating food," is displayed, indicating the snake's
successful consumption of the food.
27
Snake
Upon collision, the next step involves repositioning the food object to another
random location. The generate_random_pos method of the food object is
invoked to achieve this. Subsequently, the position of the food object is updated
using food.position = food.GenerateRandomPos() to relocate it to a new
random position.
However, a potential issue arises where the new food position might overlap
with the snake's body. To address this concern, the GenerateRandomPos()
method within the Food class is refined to verify if the newly generated position
already exists within the collection of cells occupied by the snake's body.
28
Snake
Upon validating these modifications and running the game, it becomes evident
that the food object is consistently positioned in a valid location, ensuring it
never spawns on top of the snake's body.
29
Snake
Progressing from the successful implementation of food consumption, the next vital
feature to integrate is enabling the snake to grow in length following each food
consumption event. Planning the snake's growth behavior is crucial before
implementation.
The desired behavior stipulates that upon consuming food, the snake extends in size by
adding an extra segment to its body. This new segment must be appended to the front
of the snake's body to create a natural visual progression. Importantly, when growing,
the snake's movement should halt momentarily to avoid an unnatural, jittery
appearance. Consequently, upon food consumption, a new segment will be added to the
front of the snake's body, and the snake's movement will not proceed during that
particular update method call.
To manage this behavior, a new attribute named addSegment is introduced within the
Snake class, initially set to false. This attribute will indicate whether the snake needs to
extend its length.
In the update method of the Snake class, the code segregates the behaviors based on the
addSegment attribute. If addSegment is true, the snake adds a new segment to its body
by utilizing the push_front() method of the deque. The new segment is positioned next
to the snake's head by employing the Vector2Add() function, which combines the
current head position (body[0]) with the direction of the snake's movement.
Post-extension, the addSegment attribute is set to false, signaling that the snake's growth
operation is completed. To mitigate code duplication, the repetitive line of code is
optimized by moving it outside the conditional statement, ensuring more streamlined
and concise code.
Upon successful implementation and testing, the snake now incrementally grows in
length with each collision with a food object, while maintaining its movement behavior.
This successful inclusion represents another significant advancement in the game's
development.
30
Snake
Snake grows
31
Snake
The next crucial aspect is verifying if the snake collides with the game window's edges,
signifying a game-over scenario. Verifying this condition involves checking if the head
of the snake surpasses the window boundaries in both the x and y axes.
Following the snake reset, the GenerateRandomPos() method of the food object is
invoked to relocate the food to a new position. The game pauses until the player presses
any keyboard key, achieved by modifying the running attribute within the Game class.
Incorporating this functionality, the update() method of the Game class now executes
these actions only when the running attribute is true. The game restarts when the user
presses any arrow key by changing the running attribute to true.
Additionally, detecting collisions between the snake's head and tail is vital. This
functionality is encapsulated within the CheckCollisionWithTail() method in the Game
class. By creating a copy of the snake's body, removing the head from this duplicate,
and subsequently comparing it with the original body segments, the method identifies
a collision if the head is within the headlessBody deque. A collision prompts the
GameOver() function to end the game.
32
Snake
Upon successful implementation and testing, the game appropriately handles collisions
with the window edges, tail collisions, and initiates the corresponding game-over
scenarios.
33
Snake
Initially, an offset variable of type integer, set to 75 pixels, is introduced. This variable
defines the width of the border.
To incorporate the border into the game window, adjustments are made to the game
window size, increasing it by 2 times the offset value to accommodate the border on
each side. This modification occurs in the code where the game window is created,
altering the width and height specifications.
For the frame, a dark green rectangle is drawn using the DrawRectangleLinesEx()
function, which outlines a rectangle with customizable thickness and color. This
function requires specific arguments: a rectangle structure, thickness, and color. The
rectangle is positioned with coordinates for its top-left corner, width, and height. To
encompass a frame of 75 pixels around the game window, the rectangle is drawn with
coordinates specified as Rectangle{offset - 5, offset - 5, cell_size * cellCount + 10,
cell_size * cellCount + 10}, accounting for the border thickness.
34
Snake
Once the frame is incorporated into the game, adjustments are necessary to correctly
position the snake and food within the gameplay area. The draw methods in both the
Snake and Food classes are updated, incorporating the offset value by adding it to the x
and y coordinates where necessary.
After integrating the frame and rectifying the object positions, the game window
displays the intended dark green frame, though the game elements now correctly
account for the border offset.
To further enhance the game's aesthetics, a title is introduced. Using the default font
provided by the raylib library, the DrawText() function facilitates the display of text on
the screen. Inside the game loop, this function is employed to render the game title,
"Maring-a Snake," positioned around 20 pixels from the top of the screen and offset +
5 pixels along the x-axis, with a font size approximately 40, and in the color dark green.
Upon execution, the game exhibits a visually appealing dark green frame around the
gameplay area, along with the game title positioned as intended. The next phase
involves integrating a scoring mechanism into the game by introducing a score attribute
within the Game class.
Adding Score
35
Snake
Additionally, to maintain accuracy, the score is reset to 0 whenever the game concludes.
Inside the GameOver() method, the score is set to zero: score = 0;.
The text to be displayed on the screen comprises the current game score (game.score),
which is an integer. To convert this integer score into text, the Raylib library provides
a function called TextFormat(“%i”, game.score). This function converts the integer
score into a textual format suitable for display purposes.
The display settings for the score text are configured as follows:
36
Snake
37
Snake
Adding Sound
In the Game class of the project, a sound feature is incorporated to enhance the gaming
experience. To implement this functionality, a new directory named "Sounds" is
established within the game folder. Inside this directory, two sound files are placed:
"eat.mp3" and "wall.mp3". The objective is to trigger these sounds when specific events
occur within the game: when the snake consumes food or collides with the wall.
In the Game class, two attributes are introduced to handle the sound files: eatSound and
wallSound, representing the sound effects for eating food and colliding with the wall,
respectively. These attributes are initialized as Sound types.
To manage the loading and unloading of these sound files, a constructor and a destructor
are created within the Game class. Inside the constructor, the InitAudioDevice()
function is invoked. This function initializes the audio device, preparing it for sound
playback. Subsequently, the two sound files, "eat.mp3" and "wall.mp3", are loaded into
the game's memory using the LoadSound() function. These sound files are associated
with the respective eatSound and wallSound attributes.
To play the loaded sound effects, the PlaySound() function is utilized. Within the
CheckCollisionWithFood() method, PlaySound(eatSound) is triggered when the snake
consumes the food. Similarly, in the GameOver() method, PlaySound(wallSound) is
invoked when the snake collides with the wall.
38
Snake
Upon running the game, the sounds are successfully integrated into the
gameplay. Players can audibly perceive the sound effects triggered by the
snake eating food or hitting the wall, thereby enhancing the immersive quality
of the gaming experience. This sound implementation enriches the game,
providing audio feedback for key in-game events and contributing to a more
engaging gameplay experience.
39
Snake
LIMITATIONS
The Snake game project, despite its educational and foundational significance,
has several limitations:
Limited Features
As a basic rendition, the game lacks advanced features found in more modern
games. It may not include diverse levels, power-ups, or additional challenges
beyond the standard mechanics of snake growth and collision detection.
Simplicity
The game's straightforward gameplay might become monotonous over time.
The absence of varied challenges or game elements might limit long-term
engagement.
Visual Complexity
Due to its basic design, the game may lack visually engaging elements. The
simplistic graphics and absence of intricate animations might not captivate
users expecting more visually appealing experiences.
Scalability
The game's design might not readily accommodate scalability for future
enhancements or extensive modifications. It may require significant
restructuring to integrate new features or more complex gameplay elements.
Limited Customization
Players might have limited options for customization. The game lacks settings
or difficulty adjustments, potentially limiting its appeal to users seeking
varied gameplay experiences.
Performance Constraints
40
Snake
Technical Constraints
The project might be confined by limitations inherent in the Raylib library or
the programming language used. Advanced functionalities or specific
requirements might not be feasible within the current framework.
User Engagement
The simplicity of the game might restrict its ability to engage players for
extended periods. Players seeking more complex or immersive gaming
experiences might find the game's simplicity lacking.
Lack of Innovation
The project, being a basic implementation of a classic game, may not offer
innovative or novel gameplay mechanics, potentially limiting its appeal to
users looking for unique gaming experiences.
41
Snake
CONCLUSION
The project journey was enriched by the invaluable guidance and support
received from mentors and educators. The knowledge imparted by the
National Institute of Electronics and Information Technology (NIELIT), Aizawl,
particularly from esteemed faculty members and project guides, played an
indispensable role in shaping a deeper understanding of game development
principles and programming intricacies.
42
Snake
#include <iostream>
#include <raylib.h>
#include <deque>
#include <raymath.h>
double lastUpdateTime = 0;
43
Snake
lastUpdateTime = currentTime;
return true;
}
return false;
}
class Snake
{
public:
deque<Vector2> body = {Vector2{6, 9}, Vector2{5, 9},
Vector2{4, 9}};
Vector2 direction = {1, 0};
bool addSegment = false;
void Draw()
{
for (unsigned int i = 0; i < body.size(); i++)
{
float x = body[i].x;
float y = body[i].y;
Rectangle segment = Rectangle{offset + x *
cellSize, offset + y * cellSize, (float)cellSize,
(float)cellSize};
DrawRectangleRounded(segment, 0.5, 6, darkGreen);
}
}
void Update()
{
body.push_front(Vector2Add(body[0], direction));
if (addSegment == true)
{
addSegment = false;
}
else
{
body.pop_back();
}
}
44
Snake
void Reset()
{
body = {Vector2{6, 9}, Vector2{5, 9}, Vector2{4, 9}};
direction = {1, 0};
}
};
class Food
{
public:
Vector2 position;
Texture2D texture;
Food(deque<Vector2> snakeBody)
{
Image image = LoadImage("Graphics/food.png");
texture = LoadTextureFromImage(image);
UnloadImage(image);
position = GenerateRandomPos(snakeBody);
}
~Food()
{
UnloadTexture(texture);
}
void Draw()
{
DrawTexture(texture, offset + position.x * cellSize,
offset + position.y * cellSize, WHITE);
}
Vector2 GenerateRandomCell()
{
float x = GetRandomValue(0, cellCount - 1);
float y = GetRandomValue(0, cellCount - 1);
return Vector2{x, y};
45
Snake
class Game
{
public:
Snake snake = Snake();
Food food = Food(snake.body);
bool running = true;
int score = 0;
Sound eatSound;
Sound wallSound;
Game()
{
InitAudioDevice();
eatSound = LoadSound("Sounds/eat.mp3");
wallSound = LoadSound("Sounds/wall.mp3");
}
~Game()
{
UnloadSound(eatSound);
UnloadSound(wallSound);
CloseAudioDevice();
}
void Draw()
{
46
Snake
food.Draw();
snake.Draw();
}
void Update()
{
if (running)
{
snake.Update();
CheckCollisionWithFood();
CheckCollisionWithEdges();
CheckCollisionWithTail();
}
}
void CheckCollisionWithFood()
{
if (Vector2Equals(snake.body[0], food.position))
{
food.position = food.GenerateRandomPos(snake.body);
snake.addSegment = true;
score++;
PlaySound(eatSound);
}
}
void CheckCollisionWithEdges()
{
if (snake.body[0].x == cellCount || snake.body[0].x ==
-1)
{
GameOver();
}
if (snake.body[0].y == cellCount || snake.body[0].y ==
-1)
{
GameOver();
}
}
47
Snake
void GameOver()
{
snake.Reset();
food.position = food.GenerateRandomPos(snake.body);
running = false;
score = 0;
PlaySound(wallSound);
}
void CheckCollisionWithTail()
{
deque<Vector2> headlessBody = snake.body;
headlessBody.pop_front();
if (ElementInDeque(snake.body[0], headlessBody))
{
GameOver();
}
}
};
int main()
{
cout << "Starting the game..." << endl;
InitWindow(2 * offset + cellSize * cellCount, 2 * offset +
cellSize * cellCount, "Retro Snake");
SetTargetFPS(60);
if (EventTriggered(0.2))
{
allowMove = true;
game.Update();
}
48
Snake
// Drawing
ClearBackground(green);
DrawRectangleLinesEx(Rectangle{(float)offset - 5,
(float)offset - 5, (float)cellSize * cellCount + 10,
(float)cellSize * cellCount + 10}, 5, darkGreen);
DrawText("Maring-a Snake", offset - 5, 20, 40,
darkGreen);
DrawText(TextFormat("%i", game.score), offset - 5,
offset + cellSize * cellCount + 10, 40, darkGreen);
49
Snake
game.Draw();
EndDrawing();
}
CloseWindow();
return 0;
}
50
Snake
References
Raylib Library
• Raylib official website: https://www.raylib.com/
• Raylib GitHub Repository: https://github.com/raysan5/raylib
51