diff --git a/Data/SpriteSheet.kra b/Data/SpriteSheet.kra new file mode 100644 index 0000000..4c6c55f Binary files /dev/null and b/Data/SpriteSheet.kra differ diff --git a/Data/SpriteSheet.png b/Data/SpriteSheet.png index 9aa6a4f..2cc26b1 100644 Binary files a/Data/SpriteSheet.png and b/Data/SpriteSheet.png differ diff --git a/GameConfiguration.hpp b/GameConfiguration.hpp index 0732006..e21dce0 100644 --- a/GameConfiguration.hpp +++ b/GameConfiguration.hpp @@ -15,6 +15,7 @@ class GameConfiguration float audioVolume; bool fullscreen; + int tickPerSec; template T Clamp(T &, T = 0.0, T = 1.0); void NormalizeVector2f(sf::Vector2f &); @@ -31,6 +32,7 @@ class GameConfiguration bool IsFullscreen(); float GetSpriteSizeMultiplier(); float GetAudioVolume(); + int GetTickPerSec(); sf::Color GetBackgroundColor(); sf::Vector2i GetScreenDimensions(); sf::Vector2i GetGridSize(); @@ -128,6 +130,7 @@ void GameConfiguration::LoadValuesFromFile(std::string path) int bgColor[3]; GetNextNonCommentLine(stream) >> gridSize.x >> coma >> gridSize.y; + GetNextNonCommentLine(stream) >> tickPerSec; GetNextNonCommentLine(stream) >> windowDimensions.x >> coma >> windowDimensions.y; LoadSpriteSheet(GetNextNonCommentLine(stream).str()); GetNextNonCommentLine(stream) >> spriteSheetCellSize.x >> coma >> spriteSheetCellSize.y; @@ -171,6 +174,8 @@ void GameConfiguration::WriteValuesToFile(std::string path) confFile << "# Grid Size (x, y) (5 - 100)\n"; confFile << gridSize.x << ", " << gridSize.y << "\n\n"; + confFile << "# Tick per Sec (1 - 60)\n"; + confFile << tickPerSec << "\n\n"; confFile << "# Window Dimensions (x, y)\n"; confFile << "# If fullscreen set to 1 this will be skipped\n"; confFile << windowDimensions.x << ", " << windowDimensions.y << "\n\n"; @@ -198,7 +203,8 @@ void GameConfiguration::LoadDefaultValues() LoadSpriteSheet("Data\\SpriteSheet.png"); spriteSheetCellSize = sf::Vector2i(64, 64); spriteSizeMultiplier = 1.0; - + tickPerSec = 4; + fullscreen = false; audioVolume = 1.0; } @@ -232,6 +238,11 @@ sf::IntRect GameConfiguration::GetSpriteRect(unsigned int index) return spriteSheetRect; } +int GameConfiguration::GetTickPerSec() +{ + return tickPerSec; +} + bool GameConfiguration::IsFullscreen() { return fullscreen; diff --git a/Grid.hpp b/Grid.hpp index bf9b939..c9fe57f 100644 --- a/Grid.hpp +++ b/Grid.hpp @@ -11,6 +11,7 @@ class Grid : public IDrawable Cell *CreateCellArray(sf::Vector2i); public: Grid(); + void ApplyGridLimitsToVector2i(sf::Vector2i &); bool SetGrid(); void UpdateCell(int, int, unsigned int, int = -1); void Draw(sf::RenderWindow *); @@ -40,7 +41,7 @@ Cell *Grid::CreateCellArray(sf::Vector2i) for (i = 0; i < cellCount; i++) { xPos = (i % gridSize.x) * cellSize.x * sizeMultiplier + offset.x; - yPos = (i / gridSize.x) * cellSize.y * sizeMultiplier + offset.y; + yPos = ((cellCount - i - 1) / gridSize.x) * cellSize.y * sizeMultiplier + offset.y; (array + i) -> Set(xPos, yPos); } @@ -48,6 +49,12 @@ Cell *Grid::CreateCellArray(sf::Vector2i) return array; } +void Grid::ApplyGridLimitsToVector2i(sf::Vector2i &vector) +{ + vector.x %= gridSize.x; if(vector.x < 0) vector.x += gridSize.x; + vector.y %= gridSize.y; if(vector.y < 0) vector.y += gridSize.y; +} + bool Grid::SetGrid() { GameConfiguration *config = GameManager::GetConfig(); diff --git a/Snake.hpp b/Snake.hpp index 91d4904..b99224f 100644 --- a/Snake.hpp +++ b/Snake.hpp @@ -1,4 +1,12 @@ #include "SynEngine.hpp" +#include + +enum CellState +{ + EmptyCell, + PointCell, + SnakeCell +}; enum PlayerSprite { @@ -23,20 +31,30 @@ class Snake : public IBehaviour private: std::vector snake; unsigned int lenght; + SnakePart *backSecond; + SnakePart *back; + SnakePart *headSecond; SnakePart *head; Timer *timer; Grid *grid; + sf::Vector2i gridSize; float timeCountdown; float timeResetValue; unsigned TickPerSec; GameConfiguration *config; + sf::Vector2i pointPosition; + void Move(); void Grow(); + void ResetPointPosition(); + int GetRandom(int, int); unsigned GetDirection(sf::Vector2i, sf::Vector2i); unsigned GetBackDirection(); + unsigned GetMoveDirection(); sf::Vector2i GetDirectionVector(unsigned); PlayerSprite GetNewSprite(unsigned &); + CellState NextCellState(); public: Snake(); void Update(); @@ -45,9 +63,7 @@ class Snake : public IBehaviour Snake::Snake() { - TickPerSec = 4; - timeResetValue = 1.0 / (float)TickPerSec; - timeCountdown = timeResetValue; + pointPosition = { 0, 0 }; // TEST lenght = 2; snake.reserve(lenght); @@ -55,44 +71,83 @@ Snake::Snake() snake.push_back({ sf::Vector2i(1 - i, 0), 0}); timer = &GameManager::timer; - head = &snake[0]; + head = backSecond = &snake[0]; + back = headSecond = &snake[1]; config = GameManager::GetConfig(); grid = &GameManager::GetGrid(); + gridSize = config -> GetGridSize(); + TickPerSec = config -> GetTickPerSec(); + timeResetValue = 1.0 / (float)TickPerSec; + timeCountdown = timeResetValue; grid -> UpdateCell(snake[0].cellPosition.x, snake[0].cellPosition.y, PlayerSprite::Head, 0); - grid -> UpdateCell(snake[1].cellPosition.x, snake[1].cellPosition.y, PlayerSprite::Straight, 0); + grid -> UpdateCell(snake[1].cellPosition.x, snake[1].cellPosition.y, PlayerSprite::Back, 0); + + ResetPointPosition(); +} + + +int Snake::GetRandom(int min, int max) +{ + int result; + static float value = 0.0; + value += GameManager::timer.GetTimePassed(); + srand(value); + result = rand() % (max - min); + result += min; + return result; +} + +void Snake::ResetPointPosition() +{ + bool isInside = false; + int i; + do + { + isInside = false; + pointPosition = sf::Vector2i(GetRandom(0, gridSize.x), GetRandom(0, gridSize.y)); + + for (i = 0; i < lenght; i++) + if(snake[i].cellPosition == pointPosition) + { + isInside = true; + break; + } + } while (isInside); + + grid -> UpdateCell(pointPosition.x, pointPosition.y, PlayerSprite::Point, 0); +} + +void Snake::Grow() +{ + sf::Vector2i secondPosition; + sf::Vector2i headPosition = head -> cellPosition; + unsigned previousDirection; + unsigned headDirection; + PlayerSprite sprite; + + headDirection = GetMoveDirection(); + headPosition += GetDirectionVector(headDirection); + grid -> ApplyGridLimitsToVector2i(headPosition); + snake.insert(snake.begin(), { headPosition, headDirection }); + lenght = snake.size(); + + previousDirection = snake[1].direction; + sprite = GetNewSprite(previousDirection); + + head = &snake[0]; headSecond = &snake[1]; + back = &snake[lenght - 1]; backSecond = &snake[lenght - 2]; + + grid -> UpdateCell(headPosition.x, headPosition.y, PlayerSprite::Head, headDirection); + grid -> UpdateCell(headSecond -> cellPosition.x, headSecond -> cellPosition.y, sprite, (int)previousDirection); } void Snake::Move() { - sf::Vector2i *backPosition = &snake[lenght - 1].cellPosition; - sf::Vector2i *secondPosition = &snake[1].cellPosition; - sf::Vector2i headPosition = head -> cellPosition; - unsigned direction; - unsigned headDirection; - PlayerSprite sprite; - - grid -> UpdateCell(backPosition -> x, -backPosition -> y, PlayerSprite::Empty); - - headDirection = head -> direction; + grid -> UpdateCell(back -> cellPosition.x, back -> cellPosition.y, PlayerSprite::Empty); snake.pop_back(); - - // Check if the new direction is opposite to the head's direction - if((GameManager::GetInputDirection() + 2) % 4 == headDirection) - headPosition += GetDirectionVector(headDirection); - else - { - headPosition += GameManager::GetInputVector(); - headDirection = GameManager::GetInputDirection(); - } - snake.insert(snake.begin(), { headPosition, headDirection }); - - direction = snake[1].direction; - sprite = GetNewSprite(direction); - - grid -> UpdateCell(headPosition.x, -headPosition.y, PlayerSprite::Head, headDirection); - grid -> UpdateCell(secondPosition -> x, -secondPosition -> y, sprite, (int)direction); - grid -> UpdateCell(backPosition -> x, -backPosition -> y, PlayerSprite::Back, GetBackDirection()); + Grow(); + grid -> UpdateCell(back -> cellPosition.x, back -> cellPosition.y, PlayerSprite::Back, GetBackDirection()); } void Snake::Update() @@ -103,30 +158,61 @@ void Snake::Update() return; } - Move(); + switch (NextCellState()) + { + case CellState::EmptyCell: Move(); break; + case CellState::PointCell: Grow(); ResetPointPosition(); break; + // case CellState::SnakeCell: GameOver(); break; + } timeCountdown = timeResetValue; } +unsigned Snake::GetMoveDirection() +{ + // Check if the new direction is opposite to the head's direction + unsigned direction = head -> direction; + unsigned inputDirection = GameManager::GetInputDirection(); + + if((inputDirection + 2) % 4 != direction) + return inputDirection; + + return direction; +} + sf::Vector2i Snake::GetDirectionVector(unsigned direction) { switch (direction) { - case 0: return sf::Vector2i(1, 0); - case 1: return sf::Vector2i(0, -1); - case 2: return sf::Vector2i(-1, 0); - case 3: return sf::Vector2i(0, 1); - default: return sf::Vector2i(1, 0); + case 0: return sf::Vector2i(1, 0); + case 1: return sf::Vector2i(0, -1); + case 2: return sf::Vector2i(-1, 0); + case 3: return sf::Vector2i(0, 1); + default: return sf::Vector2i(1, 0); } } +CellState Snake::NextCellState() +{ + sf::Vector2i cellPosition = head -> cellPosition + GetDirectionVector(GetMoveDirection()); + grid -> ApplyGridLimitsToVector2i(cellPosition); + + for (int i = 0; i < lenght; i++) + if(snake[i].cellPosition == cellPosition) + return CellState::SnakeCell; + + if(cellPosition == pointPosition) + return CellState::PointCell; + + return CellState::EmptyCell; +} + PlayerSprite Snake::GetNewSprite(unsigned &direction) { SnakePart *third = &snake[2]; - SnakePart *second = &snake[1]; + SnakePart *second = headSecond; sf::Vector2i vector = third -> cellPosition - head -> cellPosition; unsigned newDirection = GameManager::GetInputDirection(); - // I need to update this. It look so bad if(vector.x != 0 && vector.y != 0) { @@ -167,6 +253,7 @@ unsigned Snake::GetDirection(sf::Vector2i first, sf::Vector2i second) unsigned Snake::GetBackDirection() { + return GetDirection(snake[lenght - 2].cellPosition, snake[lenght - 1].cellPosition); }