I'm studying the book SFML Game Development by example and I'm having trouble understanding where the snake class is getting the size value for the Snake, I suspect that it's in the world.cpp and is initialized by m_blockSize=16, not really sure, I take a look at the snake class at the snake.h and there's no value at int m_size, so
what's going on? is it being initialized by the constructor member initialization list at Game.cpp? could anyone explain how the snake is gettting it's size? where is the snake getting the value?
Game.h
#pragma once
#include "Window.h"
#include "World.h"
#include "Snake.h"
class Game {
public:
Game();
~Game();
void HandleInput();
void Update();
void Render();
sf::Time GetElapsed();
void RestartClock();
Window* GetWindow();
private:
Window m_window;
sf::Clock m_clock;
float m_elapsed;
World m_world;
Snake m_snake;
};
Game.cpp
#include "Game.h"
Game::Game() : m_window("Snake", sf::Vector2u(800, 600)), m_snake(m_world.GetBlockSize()), m_world(sf::Vector2u(800, 600))
{
m_clock.restart();
m_elapsed = 0.0f;
}
Game::~Game() {}
sf::Time Game::GetElapsed() { return m_clock.getElapsedTime(); }
void Game::RestartClock() { m_elapsed += m_clock.restart().asSeconds(); }
Window* Game::GetWindow(){ return &m_window; }
void Game::HandleInput() {
// Input handling.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) && m_snake.GetPhysicalDirection() != Direction::Down)
{m_snake.SetDirection(Direction::Up);}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)
&& m_snake.GetPhysicalDirection() != Direction::Up) {
m_snake.SetDirection(Direction::Down);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)
&& m_snake.GetPhysicalDirection() != Direction::Right) {
m_snake.SetDirection(Direction::Left);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)
&& m_snake.GetPhysicalDirection() != Direction::Left) {
m_snake.SetDirection(Direction::Right);
}
}
void Game::Update() {
m_window.Update();
float timestep = 1.0f / m_snake.GetSpeed();
if (m_elapsed >= timestep) {
m_snake.Tick();
m_world.Update(m_snake);
m_elapsed -= timestep;
if (m_snake.HasLost()) {
m_snake.Reset();
}
}
}
void Game::Render() {
m_window.BeginDraw();
// Render here.
m_world.Render(*m_window.GetRenderWindow());
m_snake.Render(*m_window.GetRenderWindow());
m_window.EndDraw();
}
World.h
#pragma once
#include <SFML/Graphics.hpp>
#include "Snake.h"
class World {
public:
World(sf::Vector2u l_windSize);
~World();
int GetBlockSize();
void RespawnApple();
void Update(Snake& l_player);
void Render(sf::RenderWindow& l_window);
private:
sf::Vector2u m_windowSize;
sf::Vector2i m_item;
int m_blockSize;
sf::CircleShape m_appleShape;
sf::RectangleShape m_bounds[4];
};
World.cpp
#include "World.h"
World::World(sf::Vector2u l_windSize) {
m_blockSize = 16;
m_windowSize = l_windSize;
RespawnApple();
m_appleShape.setFillColor(sf::Color::Red);
m_appleShape.setRadius(m_blockSize / 2);
for (int i = 0; i < 4; ++i) {
m_bounds[i].setFillColor(sf::Color(150, 0, 0));
if (!((i + 1) % 2)) {
m_bounds[i].setSize(sf::Vector2f(m_windowSize.x, m_blockSize));
}
else {
m_bounds[i].setSize(sf::Vector2f(m_blockSize, m_windowSize.y));
}
if (i < 2) {
m_bounds[i].setPosition(0, 0);
}
else {
m_bounds[i].setOrigin(m_bounds[i].getSize());
m_bounds[i].setPosition(sf::Vector2f(m_windowSize));
}
}
}
World::~World() {}
int World::GetBlockSize() { return m_blockSize; }
void World::RespawnApple() {
int maxX = (m_windowSize.x / m_blockSize) - 2;
int maxY = (m_windowSize.y / m_blockSize) - 2;
m_item = sf::Vector2i(
rand() % maxX + 1, rand() % maxY + 1);
m_appleShape.setPosition(
m_item.x * m_blockSize,
m_item.y * m_blockSize);
}
void World::Update(Snake& l_player) {
if (l_player.GetPosition() == m_item) {
l_player.Extend();
l_player.IncreaseScore();
RespawnApple();
}
int gridSize_x = m_windowSize.x / m_blockSize;
int gridSize_y = m_windowSize.y / m_blockSize;
if (l_player.GetPosition().x <= 0 ||
l_player.GetPosition().y <= 0 ||
l_player.GetPosition().x >= gridSize_x - 1 ||
l_player.GetPosition().y >= gridSize_y - 1)
{
l_player.Lose();
}
}
void World::Render(sf::RenderWindow& l_window) {
for (int i = 0; i < 4; ++i) {
l_window.draw(m_bounds[i]);
}
l_window.draw(m_appleShape);
}
Snake.h
#pragma once
#include <SFML/Graphics.hpp>
#include <vector>
struct SnakeSegment
{
SnakeSegment(int x, int y) : position(x, y)
{
}
sf::Vector2i position;
};
using SnakeContainer = std::vector<SnakeSegment>;
enum class Direction { None, Up, Down, Left, Right };
class Snake {
public:
Snake(int l_blockSize);
~Snake();
// Helper methods.
void SetDirection(Direction l_dir);
Direction GetDirection();
int GetSpeed();
sf::Vector2i GetPosition();
int GetLives();
int GetScore();
void IncreaseScore();
bool HasLost();
void Lose(); // Handle losing here.
void ToggleLost();
Direction GetPhysicalDirection();
void Extend(); // Grow the snake.
void Reset(); // Reset to starting position.
void Move(); // Movement method.
void Tick(); // Update method.
void Cut(int l_segments); // Method for cutting snake.
void Render(sf::RenderWindow& l_window);
private:
void CheckCollision(); // Checking collisions.
SnakeContainer m_snakeBody; // Segment vector.
int m_size; // Size of the graphics.
Direction m_dir; // Current direction.
int m_speed; // Speed of the snake.
int m_lives; // Lives.
int m_score; // Score.
bool m_lost; // Losing state.
sf::RectangleShape m_bodyRect; // Shape used in rendering.
};
Snake.cpp
#include "Snake.h"
Snake::Snake(int l_blockSize) {
m_size = l_blockSize; m_bodyRect.setSize(sf::Vector2f(m_size -1 , m_size -1 ));
Reset();
}
Snake::~Snake() {}
void Snake::SetDirection(Direction l_dir) { m_dir = l_dir; }
Direction Snake::GetDirection() { return m_dir; }
int Snake::GetSpeed() { return m_speed; }
sf::Vector2i Snake::GetPosition() {
return (!m_snakeBody.empty() ? m_snakeBody.front().position : sf::Vector2i(1, 1));
}
int Snake::GetLives() { return m_lives; }
int Snake::GetScore() { return m_score; }
void Snake::IncreaseScore() {m_score += 10;}
bool Snake::HasLost() { return m_lost; }
void Snake::Lose() { m_lost = true; }
void Snake::ToggleLost() { m_lost = !m_lost; }
Direction Snake::GetPhysicalDirection() {
if (m_snakeBody.size() <= 1) {
return Direction::None;
}
SnakeSegment& head = m_snakeBody[0];
SnakeSegment& neck = m_snakeBody[1];
if (head.position.x == neck.position.x) {
return (head.position.y > neck.position.y
? Direction::Down : Direction::Up);
}
else if (head.position.y == neck.position.y) {
return (head.position.x > neck.position.x
? Direction::Right : Direction::Left);
}
return Direction::None;
}
void Snake::Extend() {
if (m_snakeBody.empty()) { return; }
SnakeSegment& tail_head =
m_snakeBody[m_snakeBody.size() - 1];
if (m_snakeBody.size() > 1)
{
SnakeSegment& tail_bone =
m_snakeBody[m_snakeBody.size() - 2];
if (tail_head.position.x == tail_bone.position.x)
{
if (tail_head.position.y > tail_bone.position.y)
{
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x, tail_head.position.y + 1));
}
else
{
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x, tail_head.position.y - 1));
}
}
else if (tail_head.position.y == tail_bone.position.y)
{
if (tail_head.position.x > tail_bone.position.x)
{
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x + 1, tail_head.position.y));
}
else
{
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x - 1, tail_head.position.y));
}
}
}
else
{
if (m_dir == Direction::Up)
{
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x, tail_head.position.y + 1));
}
else if (m_dir == Direction::Down) {
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x, tail_head.position.y - 1));
}
else if (m_dir == Direction::Left) {
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x + 1, tail_head.position.y));
}
else if (m_dir == Direction::Right) {
m_snakeBody.push_back(SnakeSegment(
tail_head.position.x - 1, tail_head.position.y));
}
}
}
void Snake::Reset() {
m_snakeBody.clear();
m_snakeBody.push_back(SnakeSegment(5, 7));
m_snakeBody.push_back(SnakeSegment(5, 6));
m_snakeBody.push_back(SnakeSegment(5, 5));
SetDirection(Direction::None); // Start off still.
m_speed = 15;
m_lives = 3;
m_score = 0;
m_lost = false;
}
void Snake::CheckCollision() {
if (m_snakeBody.size() < 5) { return; }
SnakeSegment& head = m_snakeBody.front();
for (auto itr = m_snakeBody.begin() + 1;
itr != m_snakeBody.end(); ++itr)
{
if (itr->position == head.position) {
int segments = m_snakeBody.end() - itr;
Cut(segments);
break;
}
}
}
void Snake::Move()
{
for (int i = m_snakeBody.size() - 1; i > 0; --i) {m_snakeBody[i].position = m_snakeBody[i - 1].position;}
if (m_dir == Direction::Left) {--m_snakeBody[0].position.x;}
else if (m_dir == Direction::Right) {++m_snakeBody[0].position.x;}
else if (m_dir == Direction::Up) {--m_snakeBody[0].position.y;}
else if (m_dir == Direction::Down) {++m_snakeBody[0].position.y;}
}
void Snake::Tick() {
if (m_snakeBody.empty()) { return; }
if (m_dir == Direction::None) { return; }
Move();
CheckCollision();
}
void Snake::Cut(int l_segments) {
for (int i = 0; i < l_segments; ++i) {
m_snakeBody.pop_back();
}
--m_lives;
if (!m_lives) { Lose(); return; }
}
void Snake::Render(sf::RenderWindow& l_window) {
if (m_snakeBody.empty()) { return; }
auto head = m_snakeBody.begin();
m_bodyRect.setFillColor(sf::Color::Yellow);
m_bodyRect.setPosition(head->position.x * m_size,
head->position.y * m_size);
l_window.draw(m_bodyRect);
m_bodyRect.setFillColor(sf::Color::Green);
for (auto itr = m_snakeBody.begin() + 1;
itr != m_snakeBody.end(); ++itr) {
m_bodyRect.setPosition(itr->position.x * m_size,
itr->position.y * m_size);
l_window.draw(m_bodyRect);
}
}