Snake++ Game [C++]
$begingroup$
Yes, It's called Snake++. I build it to learn C++.
What features and techniques can be improved? I used SDL for some basic rendering, but my concern is more about the language use.
Things I'm very concerned:
- Generating a new food means trying random positions over and over again till one is free. This is going to become a problem very soon. What data structures can I use here?
- Am I using references to their full potential and avoiding unnecessary copying?
Main.cpp
#include <iostream>
#include "Game.hpp"
using namespace std;
int main(int argc, char * argv)
{
Game game = Game();
Game().Run();
cout << "Game has terminated successfully, score: " << game.GetScore()
<< ", size: " << game.GetSize() << endl;
return 0;
}
Game.hpp
#pragma once
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
class Game
{
public:
Game();
void Run();
int GetScore();
int GetSize();
private:
bool running = false;
bool alive = false;
int fps = 0;
static const int FRAME_RATE = 1000 / 60;
static const int SCREEN_WIDTH = 640;
static const int SCREEN_HEIGHT = 640;
static const int GRID_WIDTH = 32;
static const int GRID_HEIGHT = 32;
SDL_Window * window = nullptr;
SDL_Renderer * renderer = nullptr;
enum class Block { head, body, food, empty };
enum class Move { up, down, left, right };
Move last_dir = Move::up;
Move dir = Move::up;
struct { float x = GRID_WIDTH / 2, y = GRID_HEIGHT / 2; } pos;
SDL_Point head = { static_cast<int>(pos.x), static_cast<int>(pos.y) };
SDL_Point food;
std::vector<SDL_Point> body;
Block grid[GRID_WIDTH][GRID_HEIGHT];
float speed = 0.5f;
int growing = 0;
int score = 0;
int size = 1;
void ReplaceFood();
void GrowBody(int quantity);
void UpdateWindowTitle();
void GameLoop();
void Render();
void Update();
void PollEvents();
void Close();
};
Game.cpp
#include <iostream>
#include <string>
#include <ctime>
#include "SDL.h"
#include "Game.hpp"
using namespace std;
Game::Game()
{
for (int i = 0; i < GRID_WIDTH; ++i)
for (int j = 0; j < GRID_HEIGHT; ++j)
{
grid[i][j] = Block::empty;
}
srand(static_cast<unsigned int>(time(0)));
}
void Game::Run()
{
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create Window
window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
cout << "Window could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL)
{
cout << "Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
alive = true;
running = true;
ReplaceFood();
GameLoop();
}
void Game::ReplaceFood()
{
int x, y;
while (true)
{
x = rand() % GRID_WIDTH;
y = rand() % GRID_HEIGHT;
if (grid[x][y] == Block::empty)
{
grid[x][y] = Block::food;
food.x = x;
food.y = y;
break;
}
}
}
void Game::GameLoop()
{
Uint32 before, second = SDL_GetTicks(), after;
int frame_time, frames = 0;
while (running)
{
before = SDL_GetTicks();
PollEvents();
Update();
Render();
frames++;
after = SDL_GetTicks();
frame_time = after - before;
if (after - second >= 1000)
{
fps = frames;
frames = 0;
second = after;
UpdateWindowTitle();
}
if (FRAME_RATE > frame_time)
{
SDL_Delay(FRAME_RATE - frame_time);
}
}
}
void Game::PollEvents()
{
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:
if (last_dir != Move::down || size == 1)
dir = Move::up;
break;
case SDLK_DOWN:
if (last_dir != Move::up || size == 1)
dir = Move::down;
break;
case SDLK_LEFT:
if (last_dir != Move::right || size == 1)
dir = Move::left;
break;
case SDLK_RIGHT:
if (last_dir != Move::left || size == 1)
dir = Move::right;
break;
}
}
}
}
int Game::GetSize()
{
return size;
}
void Game::GrowBody(int quantity)
{
growing += quantity;
}
void Game::Update()
{
if (!alive)
return;
switch (dir)
{
case Move::up:
pos.y -= speed;
pos.x = floorf(pos.x);
break;
case Move::down:
pos.y += speed;
pos.x = floorf(pos.x);
break;
case Move::left:
pos.x -= speed;
pos.y = floorf(pos.y);
break;
case Move::right:
pos.x += speed;
pos.y = floorf(pos.y);
break;
}
// Wrap
if (pos.x < 0) pos.x = GRID_WIDTH - 1;
else if (pos.x > GRID_WIDTH - 1) pos.x = 0;
if (pos.y < 0) pos.y = GRID_HEIGHT - 1;
else if (pos.y > GRID_HEIGHT - 1) pos.y = 0;
int new_x = static_cast<int>(pos.x);
int new_y = static_cast<int>(pos.y);
// Check if head position has changed
if (new_x != head.x || new_y != head.y)
{
last_dir = dir;
// If we are growing, just make a new neck
if (growing > 0)
{
size++;
body.push_back(head);
growing--;
grid[head.x][head.y] = Block::body;
}
else
{
// We need to shift the body
SDL_Point free = head;
vector<SDL_Point>::reverse_iterator rit = body.rbegin();
for ( ; rit != body.rend(); ++rit)
{
grid[free.x][free.y] = Block::body;
swap(*rit, free);
}
grid[free.x][free.y] = Block::empty;
}
}
head.x = new_x;
head.y = new_y;
Block & next = grid[head.x][head.y];
// Check if there's food over here
if (next == Block::food)
{
score++;
ReplaceFood();
GrowBody(1);
}
// Check if we're dead
else if (next == Block::body)
{
alive = false;
}
next = Block::head;
}
int Game::GetScore()
{
return score;
}
void Game::UpdateWindowTitle()
{
string title = "Snakle++ Score: " + to_string(score) + " FPS: " + to_string(fps);
SDL_SetWindowTitle(window, title.c_str());
}
void Game::Render()
{
SDL_Rect block;
block.w = SCREEN_WIDTH / GRID_WIDTH;
block.h = SCREEN_WIDTH / GRID_HEIGHT;
// Clear screen
SDL_SetRenderDrawColor(renderer, 0x1E, 0x1E, 0x1E, 0xFF);
SDL_RenderClear(renderer);
// Render food
SDL_SetRenderDrawColor(renderer, 0xFF, 0xCC, 0x00, 0xFF);
block.x = food.x * block.w;
block.y = food.y * block.h;
SDL_RenderFillRect(renderer, &block);
// Render snake's body
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
for (SDL_Point & point : body)
{
block.x = point.x * block.w;
block.y = point.y * block.h;
SDL_RenderFillRect(renderer, &block);
}
// Render snake's head
block.x = head.x * block.w;
block.y = head.y * block.h;
if (alive) SDL_SetRenderDrawColor(renderer, 0x00, 0x7A, 0xCC, 0xFF);
else SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &block);
// Update Screen
SDL_RenderPresent(renderer);
}
void Game::Close()
{
SDL_DestroyWindow(window);
SDL_Quit();
}
c++ beginner snake
$endgroup$
add a comment |
$begingroup$
Yes, It's called Snake++. I build it to learn C++.
What features and techniques can be improved? I used SDL for some basic rendering, but my concern is more about the language use.
Things I'm very concerned:
- Generating a new food means trying random positions over and over again till one is free. This is going to become a problem very soon. What data structures can I use here?
- Am I using references to their full potential and avoiding unnecessary copying?
Main.cpp
#include <iostream>
#include "Game.hpp"
using namespace std;
int main(int argc, char * argv)
{
Game game = Game();
Game().Run();
cout << "Game has terminated successfully, score: " << game.GetScore()
<< ", size: " << game.GetSize() << endl;
return 0;
}
Game.hpp
#pragma once
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
class Game
{
public:
Game();
void Run();
int GetScore();
int GetSize();
private:
bool running = false;
bool alive = false;
int fps = 0;
static const int FRAME_RATE = 1000 / 60;
static const int SCREEN_WIDTH = 640;
static const int SCREEN_HEIGHT = 640;
static const int GRID_WIDTH = 32;
static const int GRID_HEIGHT = 32;
SDL_Window * window = nullptr;
SDL_Renderer * renderer = nullptr;
enum class Block { head, body, food, empty };
enum class Move { up, down, left, right };
Move last_dir = Move::up;
Move dir = Move::up;
struct { float x = GRID_WIDTH / 2, y = GRID_HEIGHT / 2; } pos;
SDL_Point head = { static_cast<int>(pos.x), static_cast<int>(pos.y) };
SDL_Point food;
std::vector<SDL_Point> body;
Block grid[GRID_WIDTH][GRID_HEIGHT];
float speed = 0.5f;
int growing = 0;
int score = 0;
int size = 1;
void ReplaceFood();
void GrowBody(int quantity);
void UpdateWindowTitle();
void GameLoop();
void Render();
void Update();
void PollEvents();
void Close();
};
Game.cpp
#include <iostream>
#include <string>
#include <ctime>
#include "SDL.h"
#include "Game.hpp"
using namespace std;
Game::Game()
{
for (int i = 0; i < GRID_WIDTH; ++i)
for (int j = 0; j < GRID_HEIGHT; ++j)
{
grid[i][j] = Block::empty;
}
srand(static_cast<unsigned int>(time(0)));
}
void Game::Run()
{
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create Window
window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
cout << "Window could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL)
{
cout << "Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
alive = true;
running = true;
ReplaceFood();
GameLoop();
}
void Game::ReplaceFood()
{
int x, y;
while (true)
{
x = rand() % GRID_WIDTH;
y = rand() % GRID_HEIGHT;
if (grid[x][y] == Block::empty)
{
grid[x][y] = Block::food;
food.x = x;
food.y = y;
break;
}
}
}
void Game::GameLoop()
{
Uint32 before, second = SDL_GetTicks(), after;
int frame_time, frames = 0;
while (running)
{
before = SDL_GetTicks();
PollEvents();
Update();
Render();
frames++;
after = SDL_GetTicks();
frame_time = after - before;
if (after - second >= 1000)
{
fps = frames;
frames = 0;
second = after;
UpdateWindowTitle();
}
if (FRAME_RATE > frame_time)
{
SDL_Delay(FRAME_RATE - frame_time);
}
}
}
void Game::PollEvents()
{
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:
if (last_dir != Move::down || size == 1)
dir = Move::up;
break;
case SDLK_DOWN:
if (last_dir != Move::up || size == 1)
dir = Move::down;
break;
case SDLK_LEFT:
if (last_dir != Move::right || size == 1)
dir = Move::left;
break;
case SDLK_RIGHT:
if (last_dir != Move::left || size == 1)
dir = Move::right;
break;
}
}
}
}
int Game::GetSize()
{
return size;
}
void Game::GrowBody(int quantity)
{
growing += quantity;
}
void Game::Update()
{
if (!alive)
return;
switch (dir)
{
case Move::up:
pos.y -= speed;
pos.x = floorf(pos.x);
break;
case Move::down:
pos.y += speed;
pos.x = floorf(pos.x);
break;
case Move::left:
pos.x -= speed;
pos.y = floorf(pos.y);
break;
case Move::right:
pos.x += speed;
pos.y = floorf(pos.y);
break;
}
// Wrap
if (pos.x < 0) pos.x = GRID_WIDTH - 1;
else if (pos.x > GRID_WIDTH - 1) pos.x = 0;
if (pos.y < 0) pos.y = GRID_HEIGHT - 1;
else if (pos.y > GRID_HEIGHT - 1) pos.y = 0;
int new_x = static_cast<int>(pos.x);
int new_y = static_cast<int>(pos.y);
// Check if head position has changed
if (new_x != head.x || new_y != head.y)
{
last_dir = dir;
// If we are growing, just make a new neck
if (growing > 0)
{
size++;
body.push_back(head);
growing--;
grid[head.x][head.y] = Block::body;
}
else
{
// We need to shift the body
SDL_Point free = head;
vector<SDL_Point>::reverse_iterator rit = body.rbegin();
for ( ; rit != body.rend(); ++rit)
{
grid[free.x][free.y] = Block::body;
swap(*rit, free);
}
grid[free.x][free.y] = Block::empty;
}
}
head.x = new_x;
head.y = new_y;
Block & next = grid[head.x][head.y];
// Check if there's food over here
if (next == Block::food)
{
score++;
ReplaceFood();
GrowBody(1);
}
// Check if we're dead
else if (next == Block::body)
{
alive = false;
}
next = Block::head;
}
int Game::GetScore()
{
return score;
}
void Game::UpdateWindowTitle()
{
string title = "Snakle++ Score: " + to_string(score) + " FPS: " + to_string(fps);
SDL_SetWindowTitle(window, title.c_str());
}
void Game::Render()
{
SDL_Rect block;
block.w = SCREEN_WIDTH / GRID_WIDTH;
block.h = SCREEN_WIDTH / GRID_HEIGHT;
// Clear screen
SDL_SetRenderDrawColor(renderer, 0x1E, 0x1E, 0x1E, 0xFF);
SDL_RenderClear(renderer);
// Render food
SDL_SetRenderDrawColor(renderer, 0xFF, 0xCC, 0x00, 0xFF);
block.x = food.x * block.w;
block.y = food.y * block.h;
SDL_RenderFillRect(renderer, &block);
// Render snake's body
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
for (SDL_Point & point : body)
{
block.x = point.x * block.w;
block.y = point.y * block.h;
SDL_RenderFillRect(renderer, &block);
}
// Render snake's head
block.x = head.x * block.w;
block.y = head.y * block.h;
if (alive) SDL_SetRenderDrawColor(renderer, 0x00, 0x7A, 0xCC, 0xFF);
else SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &block);
// Update Screen
SDL_RenderPresent(renderer);
}
void Game::Close()
{
SDL_DestroyWindow(window);
SDL_Quit();
}
c++ beginner snake
$endgroup$
add a comment |
$begingroup$
Yes, It's called Snake++. I build it to learn C++.
What features and techniques can be improved? I used SDL for some basic rendering, but my concern is more about the language use.
Things I'm very concerned:
- Generating a new food means trying random positions over and over again till one is free. This is going to become a problem very soon. What data structures can I use here?
- Am I using references to their full potential and avoiding unnecessary copying?
Main.cpp
#include <iostream>
#include "Game.hpp"
using namespace std;
int main(int argc, char * argv)
{
Game game = Game();
Game().Run();
cout << "Game has terminated successfully, score: " << game.GetScore()
<< ", size: " << game.GetSize() << endl;
return 0;
}
Game.hpp
#pragma once
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
class Game
{
public:
Game();
void Run();
int GetScore();
int GetSize();
private:
bool running = false;
bool alive = false;
int fps = 0;
static const int FRAME_RATE = 1000 / 60;
static const int SCREEN_WIDTH = 640;
static const int SCREEN_HEIGHT = 640;
static const int GRID_WIDTH = 32;
static const int GRID_HEIGHT = 32;
SDL_Window * window = nullptr;
SDL_Renderer * renderer = nullptr;
enum class Block { head, body, food, empty };
enum class Move { up, down, left, right };
Move last_dir = Move::up;
Move dir = Move::up;
struct { float x = GRID_WIDTH / 2, y = GRID_HEIGHT / 2; } pos;
SDL_Point head = { static_cast<int>(pos.x), static_cast<int>(pos.y) };
SDL_Point food;
std::vector<SDL_Point> body;
Block grid[GRID_WIDTH][GRID_HEIGHT];
float speed = 0.5f;
int growing = 0;
int score = 0;
int size = 1;
void ReplaceFood();
void GrowBody(int quantity);
void UpdateWindowTitle();
void GameLoop();
void Render();
void Update();
void PollEvents();
void Close();
};
Game.cpp
#include <iostream>
#include <string>
#include <ctime>
#include "SDL.h"
#include "Game.hpp"
using namespace std;
Game::Game()
{
for (int i = 0; i < GRID_WIDTH; ++i)
for (int j = 0; j < GRID_HEIGHT; ++j)
{
grid[i][j] = Block::empty;
}
srand(static_cast<unsigned int>(time(0)));
}
void Game::Run()
{
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create Window
window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
cout << "Window could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL)
{
cout << "Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
alive = true;
running = true;
ReplaceFood();
GameLoop();
}
void Game::ReplaceFood()
{
int x, y;
while (true)
{
x = rand() % GRID_WIDTH;
y = rand() % GRID_HEIGHT;
if (grid[x][y] == Block::empty)
{
grid[x][y] = Block::food;
food.x = x;
food.y = y;
break;
}
}
}
void Game::GameLoop()
{
Uint32 before, second = SDL_GetTicks(), after;
int frame_time, frames = 0;
while (running)
{
before = SDL_GetTicks();
PollEvents();
Update();
Render();
frames++;
after = SDL_GetTicks();
frame_time = after - before;
if (after - second >= 1000)
{
fps = frames;
frames = 0;
second = after;
UpdateWindowTitle();
}
if (FRAME_RATE > frame_time)
{
SDL_Delay(FRAME_RATE - frame_time);
}
}
}
void Game::PollEvents()
{
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:
if (last_dir != Move::down || size == 1)
dir = Move::up;
break;
case SDLK_DOWN:
if (last_dir != Move::up || size == 1)
dir = Move::down;
break;
case SDLK_LEFT:
if (last_dir != Move::right || size == 1)
dir = Move::left;
break;
case SDLK_RIGHT:
if (last_dir != Move::left || size == 1)
dir = Move::right;
break;
}
}
}
}
int Game::GetSize()
{
return size;
}
void Game::GrowBody(int quantity)
{
growing += quantity;
}
void Game::Update()
{
if (!alive)
return;
switch (dir)
{
case Move::up:
pos.y -= speed;
pos.x = floorf(pos.x);
break;
case Move::down:
pos.y += speed;
pos.x = floorf(pos.x);
break;
case Move::left:
pos.x -= speed;
pos.y = floorf(pos.y);
break;
case Move::right:
pos.x += speed;
pos.y = floorf(pos.y);
break;
}
// Wrap
if (pos.x < 0) pos.x = GRID_WIDTH - 1;
else if (pos.x > GRID_WIDTH - 1) pos.x = 0;
if (pos.y < 0) pos.y = GRID_HEIGHT - 1;
else if (pos.y > GRID_HEIGHT - 1) pos.y = 0;
int new_x = static_cast<int>(pos.x);
int new_y = static_cast<int>(pos.y);
// Check if head position has changed
if (new_x != head.x || new_y != head.y)
{
last_dir = dir;
// If we are growing, just make a new neck
if (growing > 0)
{
size++;
body.push_back(head);
growing--;
grid[head.x][head.y] = Block::body;
}
else
{
// We need to shift the body
SDL_Point free = head;
vector<SDL_Point>::reverse_iterator rit = body.rbegin();
for ( ; rit != body.rend(); ++rit)
{
grid[free.x][free.y] = Block::body;
swap(*rit, free);
}
grid[free.x][free.y] = Block::empty;
}
}
head.x = new_x;
head.y = new_y;
Block & next = grid[head.x][head.y];
// Check if there's food over here
if (next == Block::food)
{
score++;
ReplaceFood();
GrowBody(1);
}
// Check if we're dead
else if (next == Block::body)
{
alive = false;
}
next = Block::head;
}
int Game::GetScore()
{
return score;
}
void Game::UpdateWindowTitle()
{
string title = "Snakle++ Score: " + to_string(score) + " FPS: " + to_string(fps);
SDL_SetWindowTitle(window, title.c_str());
}
void Game::Render()
{
SDL_Rect block;
block.w = SCREEN_WIDTH / GRID_WIDTH;
block.h = SCREEN_WIDTH / GRID_HEIGHT;
// Clear screen
SDL_SetRenderDrawColor(renderer, 0x1E, 0x1E, 0x1E, 0xFF);
SDL_RenderClear(renderer);
// Render food
SDL_SetRenderDrawColor(renderer, 0xFF, 0xCC, 0x00, 0xFF);
block.x = food.x * block.w;
block.y = food.y * block.h;
SDL_RenderFillRect(renderer, &block);
// Render snake's body
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
for (SDL_Point & point : body)
{
block.x = point.x * block.w;
block.y = point.y * block.h;
SDL_RenderFillRect(renderer, &block);
}
// Render snake's head
block.x = head.x * block.w;
block.y = head.y * block.h;
if (alive) SDL_SetRenderDrawColor(renderer, 0x00, 0x7A, 0xCC, 0xFF);
else SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &block);
// Update Screen
SDL_RenderPresent(renderer);
}
void Game::Close()
{
SDL_DestroyWindow(window);
SDL_Quit();
}
c++ beginner snake
$endgroup$
Yes, It's called Snake++. I build it to learn C++.
What features and techniques can be improved? I used SDL for some basic rendering, but my concern is more about the language use.
Things I'm very concerned:
- Generating a new food means trying random positions over and over again till one is free. This is going to become a problem very soon. What data structures can I use here?
- Am I using references to their full potential and avoiding unnecessary copying?
Main.cpp
#include <iostream>
#include "Game.hpp"
using namespace std;
int main(int argc, char * argv)
{
Game game = Game();
Game().Run();
cout << "Game has terminated successfully, score: " << game.GetScore()
<< ", size: " << game.GetSize() << endl;
return 0;
}
Game.hpp
#pragma once
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
class Game
{
public:
Game();
void Run();
int GetScore();
int GetSize();
private:
bool running = false;
bool alive = false;
int fps = 0;
static const int FRAME_RATE = 1000 / 60;
static const int SCREEN_WIDTH = 640;
static const int SCREEN_HEIGHT = 640;
static const int GRID_WIDTH = 32;
static const int GRID_HEIGHT = 32;
SDL_Window * window = nullptr;
SDL_Renderer * renderer = nullptr;
enum class Block { head, body, food, empty };
enum class Move { up, down, left, right };
Move last_dir = Move::up;
Move dir = Move::up;
struct { float x = GRID_WIDTH / 2, y = GRID_HEIGHT / 2; } pos;
SDL_Point head = { static_cast<int>(pos.x), static_cast<int>(pos.y) };
SDL_Point food;
std::vector<SDL_Point> body;
Block grid[GRID_WIDTH][GRID_HEIGHT];
float speed = 0.5f;
int growing = 0;
int score = 0;
int size = 1;
void ReplaceFood();
void GrowBody(int quantity);
void UpdateWindowTitle();
void GameLoop();
void Render();
void Update();
void PollEvents();
void Close();
};
Game.cpp
#include <iostream>
#include <string>
#include <ctime>
#include "SDL.h"
#include "Game.hpp"
using namespace std;
Game::Game()
{
for (int i = 0; i < GRID_WIDTH; ++i)
for (int j = 0; j < GRID_HEIGHT; ++j)
{
grid[i][j] = Block::empty;
}
srand(static_cast<unsigned int>(time(0)));
}
void Game::Run()
{
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create Window
window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
cout << "Window could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL)
{
cout << "Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
exit(EXIT_FAILURE);
}
alive = true;
running = true;
ReplaceFood();
GameLoop();
}
void Game::ReplaceFood()
{
int x, y;
while (true)
{
x = rand() % GRID_WIDTH;
y = rand() % GRID_HEIGHT;
if (grid[x][y] == Block::empty)
{
grid[x][y] = Block::food;
food.x = x;
food.y = y;
break;
}
}
}
void Game::GameLoop()
{
Uint32 before, second = SDL_GetTicks(), after;
int frame_time, frames = 0;
while (running)
{
before = SDL_GetTicks();
PollEvents();
Update();
Render();
frames++;
after = SDL_GetTicks();
frame_time = after - before;
if (after - second >= 1000)
{
fps = frames;
frames = 0;
second = after;
UpdateWindowTitle();
}
if (FRAME_RATE > frame_time)
{
SDL_Delay(FRAME_RATE - frame_time);
}
}
}
void Game::PollEvents()
{
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:
if (last_dir != Move::down || size == 1)
dir = Move::up;
break;
case SDLK_DOWN:
if (last_dir != Move::up || size == 1)
dir = Move::down;
break;
case SDLK_LEFT:
if (last_dir != Move::right || size == 1)
dir = Move::left;
break;
case SDLK_RIGHT:
if (last_dir != Move::left || size == 1)
dir = Move::right;
break;
}
}
}
}
int Game::GetSize()
{
return size;
}
void Game::GrowBody(int quantity)
{
growing += quantity;
}
void Game::Update()
{
if (!alive)
return;
switch (dir)
{
case Move::up:
pos.y -= speed;
pos.x = floorf(pos.x);
break;
case Move::down:
pos.y += speed;
pos.x = floorf(pos.x);
break;
case Move::left:
pos.x -= speed;
pos.y = floorf(pos.y);
break;
case Move::right:
pos.x += speed;
pos.y = floorf(pos.y);
break;
}
// Wrap
if (pos.x < 0) pos.x = GRID_WIDTH - 1;
else if (pos.x > GRID_WIDTH - 1) pos.x = 0;
if (pos.y < 0) pos.y = GRID_HEIGHT - 1;
else if (pos.y > GRID_HEIGHT - 1) pos.y = 0;
int new_x = static_cast<int>(pos.x);
int new_y = static_cast<int>(pos.y);
// Check if head position has changed
if (new_x != head.x || new_y != head.y)
{
last_dir = dir;
// If we are growing, just make a new neck
if (growing > 0)
{
size++;
body.push_back(head);
growing--;
grid[head.x][head.y] = Block::body;
}
else
{
// We need to shift the body
SDL_Point free = head;
vector<SDL_Point>::reverse_iterator rit = body.rbegin();
for ( ; rit != body.rend(); ++rit)
{
grid[free.x][free.y] = Block::body;
swap(*rit, free);
}
grid[free.x][free.y] = Block::empty;
}
}
head.x = new_x;
head.y = new_y;
Block & next = grid[head.x][head.y];
// Check if there's food over here
if (next == Block::food)
{
score++;
ReplaceFood();
GrowBody(1);
}
// Check if we're dead
else if (next == Block::body)
{
alive = false;
}
next = Block::head;
}
int Game::GetScore()
{
return score;
}
void Game::UpdateWindowTitle()
{
string title = "Snakle++ Score: " + to_string(score) + " FPS: " + to_string(fps);
SDL_SetWindowTitle(window, title.c_str());
}
void Game::Render()
{
SDL_Rect block;
block.w = SCREEN_WIDTH / GRID_WIDTH;
block.h = SCREEN_WIDTH / GRID_HEIGHT;
// Clear screen
SDL_SetRenderDrawColor(renderer, 0x1E, 0x1E, 0x1E, 0xFF);
SDL_RenderClear(renderer);
// Render food
SDL_SetRenderDrawColor(renderer, 0xFF, 0xCC, 0x00, 0xFF);
block.x = food.x * block.w;
block.y = food.y * block.h;
SDL_RenderFillRect(renderer, &block);
// Render snake's body
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
for (SDL_Point & point : body)
{
block.x = point.x * block.w;
block.y = point.y * block.h;
SDL_RenderFillRect(renderer, &block);
}
// Render snake's head
block.x = head.x * block.w;
block.y = head.y * block.h;
if (alive) SDL_SetRenderDrawColor(renderer, 0x00, 0x7A, 0xCC, 0xFF);
else SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &block);
// Update Screen
SDL_RenderPresent(renderer);
}
void Game::Close()
{
SDL_DestroyWindow(window);
SDL_Quit();
}
c++ beginner snake
c++ beginner snake
asked 50 mins ago
Afonso MatosAfonso Matos
38028
38028
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212296%2fsnake-game-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212296%2fsnake-game-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown