game is now kept in a square play area, the window can be resized and the game objects will be resized and repositioned accordingly
This commit is contained in:
parent
27d074401a
commit
ef0a139e70
|
|
@ -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
|
||||
gridCount 8
|
||||
|
|
@ -11,6 +11,6 @@ struct Fruit
|
|||
sf::Vector2u gridPos{};
|
||||
Color color{};
|
||||
|
||||
sf::Vector2f windowPos(float gridSize_in) const;
|
||||
void respawn(const std::vector<SnakeNode>& body_in, sf::Vector2u gridCount_in);
|
||||
[[nodiscard]] sf::Vector2f windowPos(sf::Vector2f gameBoundsOrigin, float size) const;
|
||||
void respawn(const std::vector<SnakeNode>& body_in, unsigned int gridCount_in);
|
||||
};
|
||||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -11,5 +11,7 @@ struct GameConfig
|
|||
Color playerHeadColor;
|
||||
Color playerBodyColor;
|
||||
sf::Vector2u headGridStartPos;
|
||||
float gridSize;
|
||||
sf::Vector2u resolution;
|
||||
unsigned int gridCount;
|
||||
float nodeSize;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T, typename U, typename V>
|
||||
inline T max(U first, V second)
|
||||
{
|
||||
return static_cast<T>(static_cast<T>(first) >= static_cast<T>(second) ? first : second);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,4 +2,3 @@
|
|||
|
||||
#include "Random.h"
|
||||
#include "Color.h"
|
||||
#include "Math-util.h"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<SnakeNode>& body_in, sf::Vector2u gridCount_in)
|
||||
void Fruit::respawn(const std::vector<SnakeNode>& body_in, unsigned int gridCount_in)
|
||||
{
|
||||
unsigned int tempX;
|
||||
unsigned int tempY;
|
||||
|
|
@ -20,8 +20,8 @@ void Fruit::respawn(const std::vector<SnakeNode>& 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)
|
||||
|
|
|
|||
121
src/Game.cpp
121
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<unsigned int>(config.gridSize);
|
||||
gridCount.y = window.getSize().y / static_cast<unsigned int>(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<float>(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<sf::Event::Resized>())
|
||||
{
|
||||
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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue