Игра работает нормально, но в ней используются прямоугольные формы, и я хочу визуализировать части змеи с помощью спрайтов. Вот код (да, он не такой уж чистый, я просто хотел, чтобы это работало):
#include // Graphics module
#include // Sound module
#include // Basic input/output (for errors)
#include // Dynamic arrays (for snake body)
#include // For random food positions
class Game {
private:
sf::Vector2u windowSize; // Size of the game window
int a, b; // Length and width of a block
sf::RenderWindow window; // Window object for drawing
sf::Font font; // Text font
sf::Clock clock; // For time measurement
std::vector body; // Snake body (segments as coordinates)
sf::Vector2i food; // Food position
sf::Vector2i direction; // Snake direction
int score; // Player's score
bool gameOver; // Game state: finished or not
int n, m; // Number of rows and columns
float delay, timer; // Update delay and elapsed time
sf::Music eat; // Sound effect for eating
public:
Game(unsigned short x, unsigned short y); // Constructor
void start(); // Start the game
private:
void loop(); // Main game loop
void events(); // Process events (keyboard, etc.)
void update(); // Update game logic
void render(); // Render elements on screen
void gameOverScreen(); // Show "Game Over" screen
sf::Vector2i getFoodPosition(); // Generate new food position
};
int WinMain() {
Game game(800, 600);
game.start();
}
int main() {
Game game(800, 600); // Create object with window size 800x600
game.start(); // Call the start function
}
Game::Game(uint16_t x, uint16_t y) {
windowSize = { x, y }; // Save window width and height
window.create(sf::VideoMode(windowSize.x, windowSize.y, 1), "Snake");
// Create the window
a = 50; // Set block width
b = 50; // Set block height
n = windowSize.x / a; // Calculate number of horizontal blocks
m = windowSize.y / b; // Calculate number of vertical blocks
font.loadFromFile("resources/Fonts/sfpro_bold.OTF"); // Load font for text
eat.openFromFile("resources/Audio/eating_apple.mp3");
}
void Game::start() {
body.clear(); // Clear the snake body
body.push_back({ 5,3 }); // Head
body.push_back({ 4,3 }); // Body segment
body.push_back({ 3,3 }); // Tail
direction = { 0, 0 }; // Direction
food = { getFoodPosition() }; // Initial food position
gameOver = false; // Game not over (false)
score = 0; // Start score from 0
loop(); // Main loop
}
void Game::loop() {
timer = 0.f; // Accumulated time
delay = 0.125f; // Game update delay
while (window.isOpen()) {
events(); // Handle user inputs (keyboard and mouse)
timer += clock.getElapsedTime().asSeconds(); // Add elapsed time in seconds to the timer
if (timer > delay) {
update(); // Update the game (move snake, etc.)
render(); // Render the screen (blocks, snake, food, text, etc.)
timer = 0; // Reset timer
}
clock.restart(); // Restart the clock for the next cycle
}
}
void Game::events() {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Escape) window.close();
else if (event.key.code == sf::Keyboard::Up && (direction.y != 1)) direction = { 0, -1 };
else if (event.key.code == sf::Keyboard::Down && (direction.y != -1)) direction = { 0, 1 };
else if (event.key.code == sf::Keyboard::Left && (direction.x != 1)) direction = { -1, 0 };
else if (event.key.code == sf::Keyboard::Right && (direction.x != -1)) direction = { 1, 0 };
else if (event.key.code == sf::Keyboard::R) /*if (gameOver)*/ start();
}
}
}
void Game::update() {
if (gameOver || direction == sf::Vector2i{ 0,0 }) return;
sf::Vector2i newHead = body[0] + direction;
for (size_t i = 1; i < body.size(); i++) {
if (newHead == body[i]) {
gameOver = true;
return;
}
}
if (newHead.x > n - 1 || newHead.x < 0 || newHead.y > m - 1 || newHead.y < 0) {
gameOver = true;
return;
}
if (newHead == food) {
body.insert(body.begin(), newHead);
food = getFoodPosition();
score++;
eat.play();
}
else {
body.insert(body.begin(), newHead);
body.pop_back();
}
}
void Game::render() {
window.clear();
sf::RectangleShape block(sf::Vector2f(a, b));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
block.setPosition(i * a, j * b);
int p = (i + j) % 2;
block.setFillColor(sf::Color(172 - p * 7, 214 - p * 7, 67 - p * 7, 255));
window.draw(block);
}
}
block.setPosition(food.x * a, food.y * b);
block.setFillColor(sf::Color::Red);
window.draw(block);
for (int i = 0; i < body.size(); i++) {
if (i == 0)
block.setFillColor(sf::Color::Magenta);
else
block.setFillColor(sf::Color(0, 71, 181));
block.setPosition(body[i].x * a, body[i].y * b);
window.draw(block);
}
sf::Text scr("Score: " + std::to_string(score), font, 32);
scr.setFillColor(sf::Color::White);
scr.setPosition(4, 0);
window.draw(scr);
if (gameOver) gameOverScreen();
window.display();
}
void Game::gameOverScreen() {
eat.stop();
sf::RectangleShape screen({ float(windowSize.x), float(windowSize.y) });
screen.setPosition({ 0,0 });
screen.setFillColor(sf::Color(0, 0, 0, 100));
sf::Text gameOverText(" Game over!\nPress R to restart", font, 38);
gameOverText.setFillColor(sf::Color::White);
gameOverText.setPosition((windowSize.x / 2) - 150, (windowSize.y / 2) - 20);
window.draw(screen);
window.draw(gameOverText);
}
sf::Vector2i Game::getFoodPosition() {
std::random_device randomDevice;
std::mt19937 randomNumberGenerator(randomDevice());
std::uniform_int_distribution distX(0, n - 1);
std::uniform_int_distribution distY(0, m - 1);
sf::Vector2i position;
do {
position.x = distX(randomNumberGenerator);
position.y = distY(randomNumberGenerator);
if (std::find(body.begin(), body.end(), position) == body.end()) {
return position;
}
} while (true);
}
В приведенном выше коде в настоящее время нет функций спрайтов, но вот подход, который я попробовал (который сработал для рендеринга спрайтов): создал карту текстур и спрайтов, загрузил текстуры. из файлов, сопоставил их со спрайтами, а затем у меня была готова карта спрайтов. Эта часть была не такой уж сложной, скорее всего, у меня были ошибки в логике рендеринга. Код был взят из тех времен, когда у меня была логика спрайтов. В основном я использую строку, чтобы определить, какой спрайт мне следует загрузить.
sf::Vector2i prev = body[i + 1]; //
sf::Vector2i next = body[i - 1]; //because i pop the tail and insert new part at the beginning
if (next.x == prev.x) {
spriteName = "body_vertical"; //vertical sprite |
}
else if (next.y == prev.y) {
spriteName = "body_horizontal"; // horizontal --
}
else {
if (prev.x < next.x) {
if (prev.y > next.y)
spriteName = "body_topright"; // start top curve right downwards
else
spriteName = "body_bottomleft"; // bottom -> left
}
else if (prev.y < next.y) {
spriteName = "body_topleft"; // top -> left
else
spriteName = "body_bottomright"; // bottom -> right
}
}
else{
if (prev.y > next.y) {
spriteName = "body_topleft"; // top -> left
else
spriteName = "body_bottomright"; // bottom -> right
}
else if (prev.y < next.y) {
spriteName = "body_topright"; // top -> right
else
spriteName = "body_bottomleft"; // bottom -> left
}
}
}
Ссылка на спрайты: opengameart.org/content/snake-game-assets. По сути, мне нужны отзывы о том, как выбрать правильный спрайт для загрузки и для какой ситуации (голова и хвост тоже будут признательны ). Спасибо!
Игра работает нормально, но в ней используются прямоугольные формы, и я хочу визуализировать части змеи с помощью спрайтов. Вот код (да, он не такой уж чистый, я просто хотел, чтобы это работало): [code]#include // Graphics module #include // Sound module #include // Basic input/output (for errors) #include // Dynamic arrays (for snake body) #include // For random food positions
class Game { private: sf::Vector2u windowSize; // Size of the game window int a, b; // Length and width of a block sf::RenderWindow window; // Window object for drawing sf::Font font; // Text font sf::Clock clock; // For time measurement std::vector body; // Snake body (segments as coordinates) sf::Vector2i food; // Food position sf::Vector2i direction; // Snake direction int score; // Player's score bool gameOver; // Game state: finished or not int n, m; // Number of rows and columns float delay, timer; // Update delay and elapsed time sf::Music eat; // Sound effect for eating
public: Game(unsigned short x, unsigned short y); // Constructor void start(); // Start the game private: void loop(); // Main game loop void events(); // Process events (keyboard, etc.) void update(); // Update game logic void render(); // Render elements on screen void gameOverScreen(); // Show "Game Over" screen sf::Vector2i getFoodPosition(); // Generate new food position };
int WinMain() { Game game(800, 600); game.start(); }
int main() { Game game(800, 600); // Create object with window size 800x600 game.start(); // Call the start function }
Game::Game(uint16_t x, uint16_t y) { windowSize = { x, y }; // Save window width and height window.create(sf::VideoMode(windowSize.x, windowSize.y, 1), "Snake"); // Create the window a = 50; // Set block width b = 50; // Set block height n = windowSize.x / a; // Calculate number of horizontal blocks m = windowSize.y / b; // Calculate number of vertical blocks font.loadFromFile("resources/Fonts/sfpro_bold.OTF"); // Load font for text eat.openFromFile("resources/Audio/eating_apple.mp3"); }
void Game::start() { body.clear(); // Clear the snake body body.push_back({ 5,3 }); // Head body.push_back({ 4,3 }); // Body segment body.push_back({ 3,3 }); // Tail direction = { 0, 0 }; // Direction food = { getFoodPosition() }; // Initial food position gameOver = false; // Game not over (false) score = 0; // Start score from 0 loop(); // Main loop }
void Game::loop() { timer = 0.f; // Accumulated time delay = 0.125f; // Game update delay while (window.isOpen()) { events(); // Handle user inputs (keyboard and mouse) timer += clock.getElapsedTime().asSeconds(); // Add elapsed time in seconds to the timer if (timer > delay) { update(); // Update the game (move snake, etc.) render(); // Render the screen (blocks, snake, food, text, etc.) timer = 0; // Reset timer } clock.restart(); // Restart the clock for the next cycle } }
void Game::events() { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::KeyPressed) { if (event.key.code == sf::Keyboard::Escape) window.close(); else if (event.key.code == sf::Keyboard::Up && (direction.y != 1)) direction = { 0, -1 }; else if (event.key.code == sf::Keyboard::Down && (direction.y != -1)) direction = { 0, 1 }; else if (event.key.code == sf::Keyboard::Left && (direction.x != 1)) direction = { -1, 0 }; else if (event.key.code == sf::Keyboard::Right && (direction.x != -1)) direction = { 1, 0 }; else if (event.key.code == sf::Keyboard::R) /*if (gameOver)*/ start(); } } }
void Game::update() { if (gameOver || direction == sf::Vector2i{ 0,0 }) return; sf::Vector2i newHead = body[0] + direction; for (size_t i = 1; i < body.size(); i++) { if (newHead == body[i]) { gameOver = true; return; } } if (newHead.x > n - 1 || newHead.x < 0 || newHead.y > m - 1 || newHead.y < 0) { gameOver = true; return; } if (newHead == food) { body.insert(body.begin(), newHead); food = getFoodPosition(); score++; eat.play(); } else { body.insert(body.begin(), newHead); body.pop_back(); } }
void Game::render() { window.clear(); sf::RectangleShape block(sf::Vector2f(a, b)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { block.setPosition(i * a, j * b); int p = (i + j) % 2; block.setFillColor(sf::Color(172 - p * 7, 214 - p * 7, 67 - p * 7, 255)); window.draw(block); } }
block.setPosition(food.x * a, food.y * b); block.setFillColor(sf::Color::Red); window.draw(block);
for (int i = 0; i < body.size(); i++) { if (i == 0) block.setFillColor(sf::Color::Magenta); else block.setFillColor(sf::Color(0, 71, 181)); block.setPosition(body[i].x * a, body[i].y * b); window.draw(block); }
std::uniform_int_distribution distX(0, n - 1); std::uniform_int_distribution distY(0, m - 1); sf::Vector2i position; do { position.x = distX(randomNumberGenerator); position.y = distY(randomNumberGenerator); if (std::find(body.begin(), body.end(), position) == body.end()) { return position; } } while (true); } [/code] В приведенном выше коде в настоящее время нет функций спрайтов, но вот подход, который я попробовал (который сработал для рендеринга спрайтов): создал карту текстур и спрайтов, загрузил текстуры. из файлов, сопоставил их со спрайтами, а затем у меня была готова карта спрайтов. Эта часть была не такой уж сложной, скорее всего, у меня были ошибки в логике рендеринга. Код был взят из тех времен, когда у меня была логика спрайтов. В основном я использую строку, чтобы определить, какой спрайт мне следует загрузить. [code]sf::Vector2i prev = body[i + 1]; // sf::Vector2i next = body[i - 1]; //because i pop the tail and insert new part at the beginning if (next.x == prev.x) { spriteName = "body_vertical"; //vertical sprite | } else if (next.y == prev.y) { spriteName = "body_horizontal"; // horizontal -- } else { if (prev.x < next.x) { if (prev.y > next.y) spriteName = "body_topright"; // start top curve right downwards else spriteName = "body_bottomleft"; // bottom -> left } else if (prev.y < next.y) { spriteName = "body_topleft"; // top -> left
else spriteName = "body_bottomright"; // bottom -> right } } else{ if (prev.y > next.y) { spriteName = "body_topleft"; // top -> left else spriteName = "body_bottomright"; // bottom -> right } else if (prev.y < next.y) { spriteName = "body_topright"; // top -> right else spriteName = "body_bottomleft"; // bottom -> left } } } [/code] Ссылка на спрайты: opengameart.org/content/snake-game-assets. По сути, мне нужны отзывы о том, как выбрать правильный спрайт для загрузки и для какой ситуации (голова и хвост тоже будут признательны 🥹). Спасибо!