diff --git a/config/game-config.txt b/config/game-config.txt index dc58bd7..02f8fdf 100644 --- a/config/game-config.txt +++ b/config/game-config.txt @@ -1,11 +1,13 @@ framerate 60 -tickRate 60 +tickRate 30 + +resolution 1920 1080 playerHeadColor 1 1 1 playerBodyColor 1 1 1 -headGridStartPos 10 10 +headGridStartPos 3 5 -gridSize 50 \ No newline at end of file +gridCount 8 \ No newline at end of file diff --git a/include/Fruit.h b/include/Fruit.h index b0a32d1..4986e0f 100644 --- a/include/Fruit.h +++ b/include/Fruit.h @@ -11,6 +11,6 @@ struct Fruit sf::Vector2u gridPos{}; Color color{}; - sf::Vector2f windowPos(float gridSize_in) const; - void respawn(const std::vector& body_in, sf::Vector2u gridCount_in); + [[nodiscard]] sf::Vector2f windowPos(sf::Vector2f gameBoundsOrigin, float size) const; + void respawn(const std::vector& body_in, unsigned int gridCount_in); }; \ No newline at end of file diff --git a/include/Game.h b/include/Game.h index 58f7715..c7f4be3 100644 --- a/include/Game.h +++ b/include/Game.h @@ -49,6 +49,8 @@ private: void winState(); + void setNewBounds(); + private: GameConfig config; @@ -70,14 +72,13 @@ private: sf::Sound moveSound; sf::RectangleShape tempRect; + sf::RectangleShape gameBoundry; //sf::Sound failSound; Player player{}; Fruit fruit{}; - sf::Vector2u gridCount; - size_t frameCount{}; size_t fruitEaten{}; GameState state{GameState::playing}; diff --git a/include/GameConfig.h b/include/GameConfig.h index 0f4be31..5a84487 100644 --- a/include/GameConfig.h +++ b/include/GameConfig.h @@ -11,5 +11,7 @@ struct GameConfig Color playerHeadColor; Color playerBodyColor; sf::Vector2u headGridStartPos; - float gridSize; + sf::Vector2u resolution; + unsigned int gridCount; + float nodeSize; }; diff --git a/include/Math-util.h b/include/Math-util.h deleted file mode 100644 index 01dcb1a..0000000 --- a/include/Math-util.h +++ /dev/null @@ -1,9 +0,0 @@ - -namespace Math -{ - template - inline T max(U first, V second) - { - return static_cast(static_cast(first) >= static_cast(second) ? first : second); - } -} \ No newline at end of file diff --git a/include/Player.h b/include/Player.h index 8a48c48..8102527 100644 --- a/include/Player.h +++ b/include/Player.h @@ -14,12 +14,11 @@ enum class Direction struct SnakeNode { SnakeNode(sf::Vector2u gridPos_in, const sf::Color& color_in = sf::Color::White); - // sf::Vector2f windowPos{}; sf::Vector2u gridPos{}; sf::Vector2u previousGridPos{}; Color color{}; - sf::Vector2f windowPos(float gridSize_in); + [[nodiscard]] sf::Vector2f windowPos(sf::Vector2f gameBoundsOrigin, float size) const; }; struct Player diff --git a/include/Util.h b/include/Util.h index 4e8db27..8606c22 100644 --- a/include/Util.h +++ b/include/Util.h @@ -1,5 +1,4 @@ #pragma once #include "Random.h" -#include "Color.h" -#include "Math-util.h" \ No newline at end of file +#include "Color.h" \ No newline at end of file diff --git a/scripts/ecc.sh b/scripts/ecc.sh index 081dbfe..d50e6d2 100755 --- a/scripts/ecc.sh +++ b/scripts/ecc.sh @@ -9,3 +9,6 @@ if [ -z "$1" ] || [ $# -eq 0 ] else ../third-party/premake5/premake5 --config=$1 ecc fi + + +mv compile_commands.json ../compile_commands.json \ No newline at end of file diff --git a/scripts/run.sh b/scripts/run.sh index 4edb154..fdddd9a 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,6 +1,6 @@ #! /bin/bash SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -cd "$SCRIPT_DIR.." +cd "$SCRIPT_DIR/.." ./bin/$1-linux-x86_64/snake diff --git a/src/Fruit.cpp b/src/Fruit.cpp index 50ee136..ad46994 100644 --- a/src/Fruit.cpp +++ b/src/Fruit.cpp @@ -7,12 +7,12 @@ Fruit::Fruit(sf::Vector2i gridPos_in, const sf::Color& color_in) , color(color_in) { } -sf::Vector2f Fruit::windowPos(float gridSize_in) const +sf::Vector2f Fruit::windowPos(sf::Vector2f gameBoundsOrigin, float size) const { - return {gridPos.x * gridSize_in, gridPos.y * gridSize_in}; + return {gameBoundsOrigin.x + (gridPos.x * size), gameBoundsOrigin.y + (gridPos.y * size)}; } -void Fruit::respawn(const std::vector& body_in, sf::Vector2u gridCount_in) +void Fruit::respawn(const std::vector& body_in, unsigned int gridCount_in) { unsigned int tempX; unsigned int tempY; @@ -20,8 +20,8 @@ void Fruit::respawn(const std::vector& body_in, sf::Vector2u gridCoun do { valid = true; - tempX = Random::get(0, gridCount_in.x - 1); - tempY = Random::get(0, gridCount_in.y - 1); + tempX = Random::get(0, gridCount_in - 1); + tempY = Random::get(0, gridCount_in - 1); for (const auto& node : body_in) { if (node.gridPos.x == tempX and node.gridPos.y == tempY) diff --git a/src/Game.cpp b/src/Game.cpp index e190d5b..661b688 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -12,7 +12,6 @@ Game::Game(bool useImgui_in) : config() - , window({sf::VideoMode({1920u, 1080u}), "project-snake"}) , score(font) , failSound(failSoundBuffer) , winSound(winSoundBuffer) @@ -21,26 +20,28 @@ Game::Game(bool useImgui_in) , moveSound(moveSoundBuffer) , useImgui(useImgui_in) { + if (!parseConfigFile()) + { + std::cout << "ERROR: Could not parse config file\n"; + return; + } + + window.create(sf::VideoMode(config.resolution), "project-snake", sf::Style::Resize | sf::Style::Close); + if (!ImGui::SFML::Init(window)) { std::cout << "ERROR: Could not open window\n"; return; } - if (!parseConfigFile()) - { - std::cout << "ERROR: Could not parse config file\n"; - window.close(); - return; - } + gameBoundry.setFillColor(sf::Color::Transparent); + gameBoundry.setOutlineColor(sf::Color::White); + setNewBounds(); - gridCount.x = window.getSize().x / static_cast(config.gridSize); - gridCount.y = window.getSize().y / static_cast(config.gridSize); - - player.body.reserve(100); + player.body.reserve(config.gridCount * config.gridCount); player.body.emplace_back(config.headGridStartPos); - fruit.gridPos = {5, 5}; + fruit.respawn(player.body, config.gridCount); fruit.color = sf::Color::Red; if (!font.openFromFile("assets/fonts/ChakraPetch-Regular.ttf")) @@ -79,11 +80,11 @@ Game::Game(bool useImgui_in) return; } - failSound.setVolume(10.f); - winSound.setVolume(10.f); - bgMusic.setVolume(10.f); - eatSound.setVolume(10.f); - moveSound.setVolume(10.f); + failSound.setVolume(config.volume); + winSound.setVolume(config.volume); + bgMusic.setVolume(config.volume); + eatSound.setVolume(config.volume); + moveSound.setVolume(config.volume); bgMusic.setLooping(true); bgMusic.play(); @@ -96,11 +97,13 @@ Game::Game(bool useImgui_in) player.body.emplace_back(sf::Vector2i{config.headGridStartPos.x - 4, config.headGridStartPos.y}); */ - tempRect.setSize(sf::Vector2f{(config.gridSize), config.gridSize}); - tempRect.setOutlineThickness(-5); + tempRect.setSize(sf::Vector2f{config.nodeSize, config.nodeSize}); + tempRect.setOutlineThickness((config.nodeSize / 10.f) * -1); tempRect.setOutlineColor(sf::Color::Black); score.setPosition({10, static_cast(window.getSize().y - 40)}); + score.setOutlineColor(sf::Color::Black); + score.setOutlineThickness(-1.f); window.setFramerateLimit(config.framerate); } @@ -148,9 +151,15 @@ bool Game::parseConfigFile() continue; } - if (inputBuff == "gridSize") + if (inputBuff == "gridCount") { - configFile >> config.gridSize; + configFile >> config.gridCount; + continue; + } + + if (inputBuff == "resolution") + { + configFile >> config.resolution.x >> config.resolution.y; continue; } } @@ -234,6 +243,11 @@ void Game::input() break; } } + + if (const auto* windowResized = event->getIf()) + { + setNewBounds(); + } } } @@ -253,11 +267,11 @@ void Game::collision() if (player.head().gridPos == fruit.gridPos) { player.body.emplace_back(player.body.back().previousGridPos); - if (player.body.size() != (gridCount.x * gridCount.y)) + if (player.body.size() != (config.gridCount * config.gridCount)) { eatSound.play(); player.score += 100; - fruit.respawn(player.body, gridCount); + fruit.respawn(player.body, config.gridCount); } else { @@ -312,7 +326,7 @@ void Game::movement() break; case Direction::down: - if (head.gridPos.y == gridCount.y - 1) + if (head.gridPos.y == config.gridCount - 1) { state = GameState::lose; failSound.play(); @@ -340,7 +354,7 @@ void Game::movement() break; case Direction::right: - if (head.gridPos.x == gridCount.x -1) + if (head.gridPos.x == config.gridCount -1) { state = GameState::lose; failSound.play(); @@ -365,8 +379,8 @@ void Game::movement() void Game::imgui() { - ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); - ImGui::SetNextWindowSize({490,375}, ImGuiCond_Once); + ImGui::SetNextWindowCollapsed(false, ImGuiCond_Once); + ImGui::SetNextWindowSize({369,374}, ImGuiCond_Once); ImGui::SetNextWindowPos({26,29}, ImGuiCond_Once); if(!ImGui::Begin("menu")) { @@ -375,13 +389,17 @@ void Game::imgui() } ImGui::Text("Controls:"); ImGui::Indent(); - ImGui::Text("A/D or arrow keys: move left and right"); + ImGui::Text("W/A/S/D or arrow keys: move"); ImGui::Text("R: reset game"); ImGui::Unindent(); if (ImGui::SliderFloat("Game Volume", &config.volume, 0, 100, "%.1f")) { - + failSound.setVolume(config.volume); + winSound.setVolume(config.volume); + bgMusic.setVolume(config.volume); + eatSound.setVolume(config.volume); + moveSound.setVolume(config.volume); } @@ -395,14 +413,16 @@ void Game::render() for (auto& node : player.body) { tempRect.setFillColor(node.color.sfml()); - tempRect.setPosition(node.windowPos(config.gridSize)); + tempRect.setPosition(node.windowPos(gameBoundry.getPosition(), config.nodeSize)); window.draw(tempRect); } tempRect.setFillColor(fruit.color.sfml()); - tempRect.setPosition(fruit.windowPos(config.gridSize)); + tempRect.setPosition(fruit.windowPos(gameBoundry.getPosition(), config.nodeSize)); window.draw(tempRect); + window.draw(gameBoundry); + score.setString("Score: " + std::to_string(player.score)); window.draw(score); @@ -425,11 +445,17 @@ void Game::soundSystem() void Game::resetGame() { + setNewBounds(); + player.body.clear(); player.body.emplace_back(config.headGridStartPos); player.facing = Direction::up; + player.inputBuffer = {}; + player.score = 0; - fruit.respawn(player.body, gridCount); + fruit.respawn(player.body, config.gridCount); + + frameCount = 0; state = GameState::playing; bgMusic.play(); @@ -453,6 +479,37 @@ void Game::winState() } } +void Game::setNewBounds() +{ + const sf::Vector2f newSize(window.getSize()); + const float minDimension = std::min(newSize.x, newSize.y); + + const sf::FloatRect visibleArea({0.f, 0.f} , newSize); + window.setView(sf::View(visibleArea)); + + // how big each node is + // use integer division to cut off remainder + config.nodeSize = minDimension / config.gridCount; + const float boundryThickness = config.nodeSize * 0.05f * -1; + + tempRect.setSize(sf::Vector2f{config.nodeSize, config.nodeSize}); + tempRect.setOutlineThickness((config.nodeSize / 10.f) * -1); + + const float boundarySize = config.gridCount * config.nodeSize; + + // Center the grid in the window + const float boundaryPosX = (newSize.x - boundarySize) / 2.f; + const float boundaryPosY = (newSize.y - boundarySize) / 2.f; + + gameBoundry.setSize({boundarySize, boundarySize}); + gameBoundry.setPosition({boundaryPosX, boundaryPosY}); + gameBoundry.setOutlineThickness(boundryThickness); + + score.setPosition({10, newSize.y - 40}); +} + + + void Game::run() { while(window.isOpen()) diff --git a/src/Player.cpp b/src/Player.cpp index a95033d..5777146 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -5,9 +5,9 @@ SnakeNode::SnakeNode(sf::Vector2u gridPos_in, const sf::Color& color_in) , color(color_in) { } -sf::Vector2f SnakeNode::windowPos(float gridSize_in) +sf::Vector2f SnakeNode::windowPos(const sf::Vector2f gameBoundsOrigin, float size) const { - return {gridPos.x * gridSize_in, gridPos.y * gridSize_in}; + return {gameBoundsOrigin.x + (gridPos.x * size), gameBoundsOrigin.y + (gridPos.y * size)}; } SnakeNode& Player::head() diff --git a/src/main.cpp b/src/main.cpp index 5a7480b..ccb95a8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,9 +14,6 @@ int main(int argc, char* argv[]) } LOG("\n\n\033[32mTODO: "); - LOG("-Implement fruit eating"); - LOG("-Implement sound effects and background music"); - LOG("-Handle game over state"); LOG("-Implement game config via imgui"); LOG("\033[0m");