Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pause feature #24

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS} src)

add_executable(SnakeGame src/main.cpp src/game.cpp src/controller.cpp src/renderer.cpp src/snake.cpp)
add_executable(SnakeGame src/player.cpp src/main.cpp src/game.cpp src/controller.cpp src/renderer.cpp src/snake.cpp)
string(STRIP ${SDL2_LIBRARIES} SDL2_LIBRARIES)
target_link_libraries(SnakeGame ${SDL2_LIBRARIES})
target_link_libraries(SnakeGame ${SDL2_LIBRARIES} SDL2_image)
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@ In this project, you can build your own C++ application or extend this Snake gam
3. Compile: `cmake .. && make`
4. Run it: `./SnakeGame`.

## New features
* Enter player's name at the beginning and stored player's data, including name and score, in the file scoreboard.txt
* Pause game when pressing ESC button, after that select 1 of 2 options: resume or exit

## Satified Requirements
1. Loop, Functions, I/O
* The project demonstrates an understanding of C++ functions and control structures.
* The project code is clearly organized into functions: All the added methods is written as functions
* The project reads data from a file and process the data, or the program writes data to a file.
* Project handles player's data and stores at in file scoreboard.txt
* The project accepts user input and processes the input.
* User enters player's name.
2. Object Oriented Programming
* The project uses Object Oriented Programming techniques.
* Class Player represents for player object
* Classes use appropriate access specifiers for class members.
* Create some set/get functions. For example: SetGameState(), GetCurrentState()
* Class constructors utilize member initialization lists.
* In class Player's constructor, initialize player name
* In renderer.cpp:40, initialize sdl_texture
3. Memory Management
* The project makes use of references in function declarations.
* In controller.cpp:15, pass game's reference to HandleInput()

## CC Attribution-ShareAlike 4.0 International

Expand Down
Binary file added pause_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 58 additions & 31 deletions src/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,67 @@
#include <iostream>
#include "SDL.h"
#include "snake.h"
#include "game.h"

void Controller::ChangeDirection(Snake &snake, Snake::Direction input,
Snake::Direction opposite) const {
if (snake.direction != opposite || snake.size == 1) snake.direction = input;
return;
Snake::Direction opposite) const
{
if (snake.direction != opposite || snake.size == 1)
snake.direction = input;
return;
}

void Controller::HandleInput(bool &running, Snake &snake) const {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
running = false;
} else if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_UP:
ChangeDirection(snake, Snake::Direction::kUp,
Snake::Direction::kDown);
break;

case SDLK_DOWN:
ChangeDirection(snake, Snake::Direction::kDown,
Snake::Direction::kUp);
break;

case SDLK_LEFT:
ChangeDirection(snake, Snake::Direction::kLeft,
Snake::Direction::kRight);
break;

case SDLK_RIGHT:
ChangeDirection(snake, Snake::Direction::kRight,
Snake::Direction::kLeft);
break;
}
void Controller::HandleInput(Game &game, Snake &snake) const
{
SDL_Event e;
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
game.SetGameState(STOPPED);
}
else if (e.type == SDL_KEYDOWN)
{
switch (e.key.keysym.sym)
{
case SDLK_UP:
ChangeDirection(snake, Snake::Direction::kUp,
Snake::Direction::kDown);
break;

case SDLK_DOWN:
ChangeDirection(snake, Snake::Direction::kDown,
Snake::Direction::kUp);
break;

case SDLK_LEFT:
ChangeDirection(snake, Snake::Direction::kLeft,
Snake::Direction::kRight);
break;

case SDLK_RIGHT:
ChangeDirection(snake, Snake::Direction::kRight,
Snake::Direction::kLeft);
break;

case SDLK_ESCAPE:
game.SetGameState(PAUSED);
break;

case SDLK_1:
if (PAUSED == game.GetCurrentState())
{
game.SetGameState(RUNNING);
}
break;

case SDLK_2:
if (PAUSED == game.GetCurrentState())
{
game.SetGameState(STOPPED);
}
break;
}
}
}
}
}
15 changes: 9 additions & 6 deletions src/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@

#include "snake.h"

class Controller {
public:
void HandleInput(bool &running, Snake &snake) const;
class Game;

private:
void ChangeDirection(Snake &snake, Snake::Direction input,
Snake::Direction opposite) const;
class Controller
{
public:
void HandleInput(Game &game, Snake &snake) const;

private:
void ChangeDirection(Snake &snake, Snake::Direction input,
Snake::Direction opposite) const;
};

#endif
160 changes: 97 additions & 63 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,116 @@ Game::Game(std::size_t grid_width, std::size_t grid_height)
: snake(grid_width, grid_height),
engine(dev()),
random_w(0, static_cast<int>(grid_width - 1)),
random_h(0, static_cast<int>(grid_height - 1)) {
PlaceFood();
random_h(0, static_cast<int>(grid_height - 1))
{
PlaceFood();
}

void Game::Run(Controller const &controller, Renderer &renderer,
std::size_t target_frame_duration) {
Uint32 title_timestamp = SDL_GetTicks();
Uint32 frame_start;
Uint32 frame_end;
Uint32 frame_duration;
int frame_count = 0;
bool running = true;

while (running) {
frame_start = SDL_GetTicks();

// Input, Update, Render - the main game loop.
controller.HandleInput(running, snake);
Update();
renderer.Render(snake, food);

frame_end = SDL_GetTicks();

// Keep track of how long each loop through the input/update/render cycle
// takes.
frame_count++;
frame_duration = frame_end - frame_start;

// After every second, update the window title.
if (frame_end - title_timestamp >= 1000) {
renderer.UpdateWindowTitle(score, frame_count);
frame_count = 0;
title_timestamp = frame_end;
}
std::size_t target_frame_duration)
{
Uint32 title_timestamp = SDL_GetTicks();
Uint32 frame_start;
Uint32 frame_end;
Uint32 frame_duration;
int frame_count = 0;

while ((state_ == RUNNING) || (state_ == PAUSED))
{
frame_start = SDL_GetTicks();

// Input, Update, Render - the main game loop.
controller.HandleInput(*this, snake);

// Display the pause menu
if (state_ == PAUSED)
{
renderer.UpdatePauseMenu();
continue;
}

Update();
renderer.Render(snake, food);

frame_end = SDL_GetTicks();

// Keep track of how long each loop through the input/update/render cycle
// takes.
frame_count++;
frame_duration = frame_end - frame_start;

// If the time for this frame is too small (i.e. frame_duration is
// smaller than the target ms_per_frame), delay the loop to
// achieve the correct frame rate.
if (frame_duration < target_frame_duration) {
SDL_Delay(target_frame_duration - frame_duration);
// After every second, update the window title.
if (frame_end - title_timestamp >= 1000)
{
renderer.UpdateWindowTitle(score, frame_count);
frame_count = 0;
title_timestamp = frame_end;
}

// If the time for this frame is too small (i.e. frame_duration is
// smaller than the target ms_per_frame), delay the loop to
// achieve the correct frame rate.
if (frame_duration < target_frame_duration)
{
SDL_Delay(target_frame_duration - frame_duration);
}
}
}
}

void Game::PlaceFood() {
int x, y;
while (true) {
x = random_w(engine);
y = random_h(engine);
// Check that the location is not occupied by a snake item before placing
// food.
if (!snake.SnakeCell(x, y)) {
food.x = x;
food.y = y;
return;
void Game::PlaceFood()
{
int x, y;
while (true)
{
x = random_w(engine);
y = random_h(engine);
// Check that the location is not occupied by a snake item before placing
// food.
if (!snake.SnakeCell(x, y))
{
food.x = x;
food.y = y;
return;
}
}
}
}

void Game::Update() {
if (!snake.alive) return;
void Game::Update()
{
if (!snake.alive)
return;

snake.Update();
snake.Update();

int new_x = static_cast<int>(snake.head_x);
int new_y = static_cast<int>(snake.head_y);
int new_x = static_cast<int>(snake.head_x);
int new_y = static_cast<int>(snake.head_y);

// Check if there's food over here
if (food.x == new_x && food.y == new_y) {
score++;
PlaceFood();
// Grow snake and increase speed.
snake.GrowBody();
snake.speed += 0.02;
}
// Check if there's food over here
if (food.x == new_x && food.y == new_y)
{
score++;
PlaceFood();
// Grow snake and increase speed.
snake.GrowBody();
snake.speed += 0.02;
}
}

Game::~Game()
{
player.SetScore(score);
player.SaveToScoreBoard();
}

int Game::GetScore() const { return score; }
int Game::GetSize() const { return snake.size; }
int Game::GetSize() const { return snake.size; }

void Game::SetGameState(GameState newState)
{
state_ = newState;
}

GameState Game::GetCurrentState()
{
return state_;
}
Loading