Migrate from SDL to SFML
This simplifies the rendering code and remove all(!) pointers. There is a number of unresolved issues * Windows build ? * position delta computation: migrating to SFML seems to have modified the frame rate.
This commit is contained in:
parent
8bcc4e176a
commit
c7ef086d3d
|
@ -67,8 +67,8 @@ bool Board::isWalkable(Position point, float position_delta, Direction direction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SDL_Point> Board::initialPelletPositions() const {
|
std::vector<PositionInt> Board::initialPelletPositions() const {
|
||||||
std::vector<SDL_Point> positions;
|
std::vector<PositionInt> positions;
|
||||||
for (uint8_t row = 0; row < ROWS; row++) {
|
for (uint8_t row = 0; row < ROWS; row++) {
|
||||||
for (uint8_t column = 0; column < COLUMNS; column++) {
|
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||||
if (board_state[row][column] == 1)
|
if (board_state[row][column] == 1)
|
||||||
|
@ -78,8 +78,8 @@ std::vector<SDL_Point> Board::initialPelletPositions() const {
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SDL_Point> Board::initialSuperPelletPositions() const {
|
std::vector<PositionInt> Board::initialSuperPelletPositions() const {
|
||||||
std::vector<SDL_Point> positions;
|
std::vector<PositionInt> positions;
|
||||||
for (uint8_t row = 0; row < ROWS; row++) {
|
for (uint8_t row = 0; row < ROWS; row++) {
|
||||||
for (uint8_t column = 0; column < COLUMNS; column++) {
|
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||||
if (board_state[row][column] == 4)
|
if (board_state[row][column] == 4)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
#include "Direction.hpp"
|
#include "Direction.hpp"
|
||||||
#include "Position.hpp"
|
#include "Position.hpp"
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool isWalkable(Position point, float d, Direction direction) const;
|
[[nodiscard]] bool isWalkable(Position point, float d, Direction direction) const;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<SDL_Point> initialPelletPositions() const;
|
[[nodiscard]] std::vector<PositionInt> initialPelletPositions() const;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<SDL_Point> initialSuperPelletPositions() const;
|
[[nodiscard]] std::vector<PositionInt> initialSuperPelletPositions() const;
|
||||||
|
|
||||||
static Position initialPacManPosition() { return { 14, 23 }; }
|
static Position initialPacManPosition() { return { 14, 23 }; }
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
find_package(sdl2-image CONFIG REQUIRED)
|
find_package(SFML COMPONENTS COMPONENTS window graphics system CONFIG REQUIRED)
|
||||||
find_package(sdl2-ttf CONFIG REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(OpenGL REQUIRED COMPONENTS OpenGL GLX)
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
|
|
||||||
|
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
|
||||||
|
|
||||||
|
add_library(libpacman ${ sources })
|
||||||
|
|
||||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
|
target_include_directories(libpacman PUBLIC.)
|
||||||
|
target_link_libraries(libpacman PUBLIC fmt::fmt sfml - graphics sfml - window sfml - system OpenGL::OpenGL OpenGL::GLX)
|
||||||
add_library(libpacman ${sources})
|
|
||||||
|
|
||||||
target_include_directories(libpacman PUBLIC .)
|
|
||||||
target_link_libraries(libpacman PUBLIC SDL2::SDL2-static SDL2::SDL2_image SDL2::SDL2_ttf fmt::fmt ZLIB::ZLIB)
|
|
||||||
|
|
118
lib/Canvas.cpp
118
lib/Canvas.cpp
|
@ -1,15 +1,23 @@
|
||||||
#include "Canvas.hpp"
|
#include "Canvas.hpp"
|
||||||
#include <vector>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include "PacMan.hpp"
|
#include "PacMan.hpp"
|
||||||
#include "Pellets.hpp"
|
#include "Pellets.hpp"
|
||||||
#include "SuperPellets.hpp"
|
#include "SuperPellets.hpp"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
Canvas::Canvas()
|
Canvas::Canvas()
|
||||||
: window(windowDimensions()) {}
|
: window(sf::VideoMode(windowDimensions().width, windowDimensions().height), "Pacman", sf::Style::Titlebar | sf::Style::Close) {
|
||||||
|
window.setVerticalSyncEnabled(true);
|
||||||
|
|
||||||
|
maze_texture = loadTexture("maze.png");
|
||||||
|
sprites_texture = loadTexture("sprites32.png");
|
||||||
|
game_font = loadFont("joystix.ttf");
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets) {
|
void Canvas::update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets) {
|
||||||
window.clear();
|
clear();
|
||||||
|
|
||||||
renderMaze();
|
renderMaze();
|
||||||
renderPellets(pellets);
|
renderPellets(pellets);
|
||||||
|
@ -17,62 +25,104 @@ void Canvas::update(const PacMan & pacMan, const Pellets & pellets, const SuperP
|
||||||
renderPacMan(pacMan);
|
renderPacMan(pacMan);
|
||||||
renderScore(0);
|
renderScore(0);
|
||||||
|
|
||||||
window.render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderMaze() const {
|
void Canvas::clear() {
|
||||||
SDL_Rect maze_translated = { LEFT_MARGIN, TOP_MARGIN, MAZE_WIDTH, MAZE_HEIGHT };
|
window.clear(sf::Color::Black);
|
||||||
Sprite maze = window.getBackground();
|
|
||||||
window.renderSprite(maze, maze_translated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderPellets(const Pellets & pellets) const {
|
void Canvas::render() {
|
||||||
|
window.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<sf::Event> Canvas::pollEvent() {
|
||||||
|
sf::Event event;
|
||||||
|
if (window.pollEvent(event))
|
||||||
|
return event;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::renderMaze() {
|
||||||
|
Sprite maze(maze_texture);
|
||||||
|
maze.setPosition(LEFT_MARGIN, TOP_MARGIN);
|
||||||
|
window.draw(maze);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::renderPellets(const Pellets & pellets) {
|
||||||
Sprite pellet = getSprite(pellets.currentSprite());
|
Sprite pellet = getSprite(pellets.currentSprite());
|
||||||
std::vector<SDL_Point> pelletPositions = pellets.currentPositions();
|
std::vector<PositionInt> pelletPositions = pellets.currentPositions();
|
||||||
for (const auto & pos : pelletPositions) {
|
for (const auto & pos : pelletPositions) {
|
||||||
renderSprite(pellet, pos);
|
renderSprite(pellet, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderSuperPellets(const SuperPellets & superPellets) const {
|
void Canvas::renderSuperPellets(const SuperPellets & superPellets) {
|
||||||
Sprite pellet = getSprite(superPellets.currentSprite());
|
Sprite pellet = getSprite(superPellets.currentSprite());
|
||||||
std::vector<SDL_Point> superPelletPositions = superPellets.currentPositions();
|
std::vector<PositionInt> superPelletPositions = superPellets.currentPositions();
|
||||||
for (const auto & pos : superPelletPositions) {
|
for (const auto & pos : superPelletPositions) {
|
||||||
renderSprite(pellet, pos);
|
renderSprite(pellet, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderPacMan(const PacMan & pac_man) const {
|
void Canvas::renderPacMan(const PacMan & pac_man) {
|
||||||
Sprite pacmanSprite = getSprite(pac_man.currentSprite());
|
Sprite pacmanSprite = getSprite(pac_man.currentSprite());
|
||||||
const auto & pos = pac_man.position();
|
const auto & pos = pac_man.position();
|
||||||
renderSprite(pacmanSprite, SDL_Point{ int(pos.x), int(pos.y) });
|
renderSprite(pacmanSprite, { int(pos.x), int(pos.y) });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderScore(int score) {
|
void Canvas::renderScore(int score) {
|
||||||
const int x = LEFT_MARGIN + MAZE_WIDTH + LEFT_MARGIN;
|
const int x = LEFT_MARGIN + MAZE_WIDTH + LEFT_MARGIN;
|
||||||
const int y = TOP_MARGIN * 2;
|
const int y = TOP_MARGIN * 2;
|
||||||
window.drawText("SCORE", {x, y});
|
|
||||||
window.drawText(fmt::format("{}", score), {x, y + 20});
|
sf::Text text;
|
||||||
|
text.setPosition(x, y);
|
||||||
|
text.setFont(game_font);
|
||||||
|
text.setString(fmt::format("SCORE\n{}", score));
|
||||||
|
text.setCharacterSize(20);
|
||||||
|
text.setFillColor(sf::Color::White);
|
||||||
|
window.draw(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Rect Canvas::windowDimensions() const {
|
Rect Canvas::windowDimensions() const {
|
||||||
return { 0, 0, LEFT_MARGIN + MAZE_WIDTH + SCORE_WIDTH, TOP_MARGIN + MAZE_HEIGHT + BOTTOM_MARGIN };
|
return { 0, 0, LEFT_MARGIN + MAZE_WIDTH + SCORE_WIDTH, TOP_MARGIN + MAZE_HEIGHT + BOTTOM_MARGIN };
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite Canvas::getSprite(SDL_Point coordinate) const {
|
Sprite Canvas::getSprite(PositionInt coordinate) const {
|
||||||
return window.getSprite(
|
sf::Sprite sprite;
|
||||||
{ coordinate.x * DEFAULT_SPRITE_WIDTH,
|
sprite.setTexture(sprites_texture);
|
||||||
coordinate.y * DEFAULT_SPRITE_HEIGHT,
|
sprite.setTextureRect(sf::IntRect{ coordinate.x * DEFAULT_SPRITE_WIDTH,
|
||||||
DEFAULT_SPRITE_WIDTH,
|
coordinate.y * DEFAULT_SPRITE_HEIGHT,
|
||||||
DEFAULT_SPRITE_HEIGHT });
|
DEFAULT_SPRITE_WIDTH,
|
||||||
|
DEFAULT_SPRITE_HEIGHT });
|
||||||
|
sprite.scale(0.5, 0.5);
|
||||||
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::renderSprite(Sprite sprite, SDL_Point point) const {
|
void Canvas::renderSprite(Sprite sprite, PositionInt pos) {
|
||||||
SDL_Rect target = {
|
pos.x = LEFT_MARGIN + (pos.x * DEFAULT_SPRITE_WIDTH / 2);
|
||||||
LEFT_MARGIN + int((point.x * DEFAULT_SPRITE_WIDTH - sprite.rect.w / 2) * TEXTURE_SCALE_FACTOR),
|
pos.y = TOP_MARGIN + (pos.y * DEFAULT_SPRITE_HEIGHT / 2);
|
||||||
TOP_MARGIN + int((point.y * DEFAULT_SPRITE_WIDTH - sprite.rect.h / 2) * TEXTURE_SCALE_FACTOR),
|
sprite.setPosition(pos.x, pos.y);
|
||||||
sprite.rect.w,
|
window.draw(sprite);
|
||||||
sprite.rect.h
|
}
|
||||||
};
|
|
||||||
window.renderSprite(sprite, target);
|
static void exitFailure(const std::string & message) {
|
||||||
|
fmt::print("{}\n", message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Texture Canvas::loadTexture(std::string_view path) {
|
||||||
|
sf::Texture texture;
|
||||||
|
if (!texture.loadFromFile(std::string{ path })) {
|
||||||
|
exitFailure(fmt::format("Failed to load image {}", path));
|
||||||
|
}
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Font Canvas::loadFont(std::string_view path) {
|
||||||
|
sf::Font font;
|
||||||
|
if (!font.loadFromFile("joystix.ttf")) {
|
||||||
|
exitFailure(fmt::format("Failed to load font {}", path));
|
||||||
|
}
|
||||||
|
return font;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "SDLWindow.hpp"
|
#include "Position.hpp"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class PacMan;
|
class PacMan;
|
||||||
class Pellets;
|
class Pellets;
|
||||||
|
@ -10,6 +11,7 @@ class Canvas {
|
||||||
public:
|
public:
|
||||||
Canvas();
|
Canvas();
|
||||||
void update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets);
|
void update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets);
|
||||||
|
std::optional<sf::Event> pollEvent();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int16_t LEFT_MARGIN = 40;
|
static constexpr int16_t LEFT_MARGIN = 40;
|
||||||
|
@ -20,16 +22,28 @@ private:
|
||||||
static constexpr int16_t SCORE_WIDTH = 200;
|
static constexpr int16_t SCORE_WIDTH = 200;
|
||||||
static constexpr int16_t DEFAULT_SPRITE_WIDTH = 32;
|
static constexpr int16_t DEFAULT_SPRITE_WIDTH = 32;
|
||||||
static constexpr int16_t DEFAULT_SPRITE_HEIGHT = 32;
|
static constexpr int16_t DEFAULT_SPRITE_HEIGHT = 32;
|
||||||
static constexpr float TEXTURE_SCALE_FACTOR = 0.5;
|
|
||||||
|
|
||||||
void renderMaze() const;
|
void clear();
|
||||||
void renderPacMan(const PacMan & pac_man) const;
|
void render();
|
||||||
void renderPellets(const Pellets & pellets) const;
|
void renderMaze();
|
||||||
void renderSuperPellets(const SuperPellets & superPellets) const;
|
void renderPacMan(const PacMan & pac_man);
|
||||||
void renderSprite(Sprite sprite, SDL_Point point) const;
|
void renderPellets(const Pellets & pellets);
|
||||||
|
void renderSuperPellets(const SuperPellets & superPellets);
|
||||||
|
void renderSprite(Sprite sprite, PositionInt point);
|
||||||
|
void renderSprite(Sprite sprite, Rect target);
|
||||||
|
|
||||||
void renderScore(int score);
|
void renderScore(int score);
|
||||||
|
|
||||||
SDL_Rect windowDimensions() const;
|
Rect windowDimensions() const;
|
||||||
Sprite getSprite(SDL_Point rect) const;
|
|
||||||
SDLWindow window;
|
void loadTextures();
|
||||||
|
sf::Texture loadTexture(std::string_view path);
|
||||||
|
sf::Font loadFont(std::string_view path);
|
||||||
|
|
||||||
|
Sprite getSprite(PositionInt rect) const;
|
||||||
|
|
||||||
|
sf::RenderWindow window;
|
||||||
|
sf::Texture maze_texture;
|
||||||
|
sf::Texture sprites_texture;
|
||||||
|
sf::Font game_font;
|
||||||
};
|
};
|
||||||
|
|
40
lib/Game.cpp
40
lib/Game.cpp
|
@ -32,35 +32,17 @@ void Game::eatPellets() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::processEvents(InputState & inputState) {
|
void Game::processEvents(InputState & inputState) {
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
switch (event.type) {
|
|
||||||
case SDL_QUIT:
|
|
||||||
inputState.close = true;
|
|
||||||
break;
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
keyToggle(event, inputState, true);
|
|
||||||
break;
|
|
||||||
case SDL_KEYUP:
|
|
||||||
keyToggle(event, inputState, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::keyToggle(const SDL_Event & event, InputState & inputState, bool on) {
|
auto event = canvas.pollEvent();
|
||||||
switch (event.key.keysym.sym) {
|
if (event->type == sf::Event::Closed) {
|
||||||
case SDLK_UP:
|
inputState.close = true;
|
||||||
inputState.up = on;
|
return;
|
||||||
break;
|
|
||||||
case SDLK_DOWN:
|
|
||||||
inputState.down = on;
|
|
||||||
break;
|
|
||||||
case SDLK_LEFT:
|
|
||||||
inputState.left = on;
|
|
||||||
break;
|
|
||||||
case SDLK_RIGHT:
|
|
||||||
inputState.right = on;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputState.down = inputState.up = inputState.left = inputState.right = false;
|
||||||
|
|
||||||
|
inputState.down = sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down);
|
||||||
|
inputState.up = sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up);
|
||||||
|
inputState.left = sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left);
|
||||||
|
inputState.right = sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,7 @@ private:
|
||||||
|
|
||||||
void eatPellets();
|
void eatPellets();
|
||||||
|
|
||||||
static void processEvents(InputState & inputState);
|
void processEvents(InputState & inputState);
|
||||||
|
|
||||||
static void keyToggle(const SDL_Event & event, InputState & inputState, bool on);
|
|
||||||
|
|
||||||
[[nodiscard]] static auto now();
|
[[nodiscard]] static auto now();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#include "PacMan.hpp"
|
#include "PacMan.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
PacMan::PacMan(const Board & board)
|
PacMan::PacMan(const Board & board)
|
||||||
: pos(board.initialPacManPosition()) {}
|
: pos(board.initialPacManPosition()) {}
|
||||||
|
|
||||||
SDL_Point PacMan::currentSprite() const {
|
PositionInt PacMan::currentSprite() const {
|
||||||
return pacManAnimation.animationFrame(direction);
|
return pacManAnimation.animationFrame(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,10 +16,10 @@ Position PacMan::positionInGrid() const {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case Direction::LEFT:
|
case Direction::LEFT:
|
||||||
case Direction::RIGHT:
|
case Direction::RIGHT:
|
||||||
return { floor(pos.x), round(pos.y) };
|
return { std::floor(pos.x), std::round(pos.y) };
|
||||||
case Direction::UP:
|
case Direction::UP:
|
||||||
case Direction::DOWN:
|
case Direction::DOWN:
|
||||||
return { floor(pos.x), round(pos.y) };
|
return { std::floor(pos.x), std::round(pos.y) };
|
||||||
default:
|
default:
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +48,7 @@ void PacMan::updateAnimationPosition(std::chrono::milliseconds time_delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacMan::updateMazePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
void PacMan::updateMazePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
float position_delta = time_delta.count() / 128.0;
|
float position_delta = time_delta.count() / 150.0;
|
||||||
|
|
||||||
// Handle teleport
|
// Handle teleport
|
||||||
if (pos.x >= COLUMNS - 1 && direction == Direction::RIGHT) {
|
if (pos.x >= COLUMNS - 1 && direction == Direction::RIGHT) {
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "PacManAnimation.hpp"
|
#include "PacManAnimation.hpp"
|
||||||
#include "Position.hpp"
|
#include "Position.hpp"
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
class Board;
|
class Board;
|
||||||
|
@ -14,7 +13,7 @@ class PacMan {
|
||||||
public:
|
public:
|
||||||
explicit PacMan(const Board & board);
|
explicit PacMan(const Board & board);
|
||||||
|
|
||||||
[[nodiscard]] SDL_Point currentSprite() const;
|
[[nodiscard]] PositionInt currentSprite() const;
|
||||||
|
|
||||||
[[nodiscard]] Position position() const;
|
[[nodiscard]] Position position() const;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "PacManAnimation.hpp"
|
#include "PacManAnimation.hpp"
|
||||||
|
|
||||||
SDL_Point PacManAnimation::animationFrame(Direction direction) const {
|
PositionInt PacManAnimation::animationFrame(Direction direction) const {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case Direction::LEFT:
|
case Direction::LEFT:
|
||||||
return left_animation[animation_position];
|
return left_animation[animation_position];
|
||||||
|
|
|
@ -5,29 +5,28 @@
|
||||||
#include "InputState.hpp"
|
#include "InputState.hpp"
|
||||||
#include "Position.hpp"
|
#include "Position.hpp"
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
class PacManAnimation {
|
class PacManAnimation {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] SDL_Point animationFrame(Direction direction) const;
|
[[nodiscard]] PositionInt animationFrame(Direction direction) const;
|
||||||
|
|
||||||
void updateAnimationPosition(std::chrono::milliseconds time_delta);
|
void updateAnimationPosition(std::chrono::milliseconds time_delta);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t animation_position = 0;
|
uint8_t animation_position = 0;
|
||||||
float animation_position_delta = 0.0;
|
float animation_position_delta = 0.0;
|
||||||
const SDL_Point right_wide = { 0, 0 };
|
const PositionInt right_wide = { 0, 0 };
|
||||||
const SDL_Point right_narrow = { 1, 0 };
|
const PositionInt right_narrow = { 1, 0 };
|
||||||
const SDL_Point closed = { 2, 0 };
|
const PositionInt closed = { 2, 0 };
|
||||||
const SDL_Point left_narrow = { 3, 0 };
|
const PositionInt left_narrow = { 3, 0 };
|
||||||
const SDL_Point left_wide = { 4, 0 };
|
const PositionInt left_wide = { 4, 0 };
|
||||||
const SDL_Point up_wide = { 5, 0 };
|
const PositionInt up_wide = { 5, 0 };
|
||||||
const SDL_Point up_narrow = { 6, 0 };
|
const PositionInt up_narrow = { 6, 0 };
|
||||||
const SDL_Point down_wide = { 7, 0 };
|
const PositionInt down_wide = { 7, 0 };
|
||||||
const SDL_Point down_narrow = { 8, 0 };
|
const PositionInt down_narrow = { 8, 0 };
|
||||||
const SDL_Point down_animation[4]{ down_wide, down_narrow, closed, down_narrow };
|
const PositionInt down_animation[4]{ down_wide, down_narrow, closed, down_narrow };
|
||||||
const SDL_Point left_animation[4]{ left_wide, left_narrow, closed, left_narrow };
|
const PositionInt left_animation[4]{ left_wide, left_narrow, closed, left_narrow };
|
||||||
const SDL_Point right_animation[4]{ right_wide, right_narrow, closed, right_narrow };
|
const PositionInt right_animation[4]{ right_wide, right_narrow, closed, right_narrow };
|
||||||
const SDL_Point up_animation[4]{ up_wide, up_narrow, closed, up_narrow };
|
const PositionInt up_animation[4]{ up_wide, up_narrow, closed, up_narrow };
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,23 +3,21 @@
|
||||||
#include "Board.hpp"
|
#include "Board.hpp"
|
||||||
#include "Position.hpp"
|
#include "Position.hpp"
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
|
||||||
|
|
||||||
class Pellets {
|
class Pellets {
|
||||||
public:
|
public:
|
||||||
explicit Pellets(const Board & board);
|
explicit Pellets(const Board & board);
|
||||||
|
|
||||||
[[nodiscard]] SDL_Point currentSprite() const {
|
[[nodiscard]] PositionInt currentSprite() const {
|
||||||
return sprite;
|
return sprite;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::vector<SDL_Point> currentPositions() const {
|
[[nodiscard]] std::vector<PositionInt> currentPositions() const {
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eatPelletAtPosition(Position p);
|
bool eatPelletAtPosition(Position p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const SDL_Point sprite = { 1, 9 };
|
const PositionInt sprite = { 1, 9 };
|
||||||
std::vector<SDL_Point> positions;
|
std::vector<PositionInt> positions;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
struct Position {
|
struct Position {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const SDL_Point & b, const Position & a) {
|
struct PositionInt {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Rect = sf::Rect<int>;
|
||||||
|
|
||||||
|
using Sprite = sf::Sprite;
|
||||||
|
|
||||||
|
inline bool operator==(const PositionInt & b, const Position & a) {
|
||||||
return a.x == b.x && a.y == b.y;
|
return a.x == b.x && a.y == b.y;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
#include "SDLWindow.hpp"
|
|
||||||
|
|
||||||
#include <SDL2/SDL_image.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
SDLWindow::SDLWindow(SDL_Rect windowGeometry) {
|
|
||||||
initSDL();
|
|
||||||
initSDLImage();
|
|
||||||
initSDLTTF();
|
|
||||||
|
|
||||||
createWindow(windowGeometry.w, windowGeometry.h);
|
|
||||||
createRenderer();
|
|
||||||
createWindowSurface();
|
|
||||||
setDrawColor();
|
|
||||||
maze_texture = loadTexture("maze.png");
|
|
||||||
sprite_texture = loadTexture("sprites32.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::clear() {
|
|
||||||
SDL_RenderClear(renderer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::render() {
|
|
||||||
SDL_RenderPresent(renderer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
Sprite SDLWindow::getBackground() const {
|
|
||||||
auto [w, h] = textureSize(maze_texture.get());
|
|
||||||
SDL_Rect maze_rect = { 0, 0, w, h };
|
|
||||||
return { maze_texture.get(), maze_rect };
|
|
||||||
}
|
|
||||||
|
|
||||||
Sprite SDLWindow::getSprite(SDL_Rect rect) const {
|
|
||||||
return { sprite_texture.get(), rect };
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::renderSprite(Sprite sprite, SDL_Rect target) const {
|
|
||||||
if (SDL_RenderCopy(renderer.get(), sprite.texture, &sprite.rect, &target) < 0)
|
|
||||||
exitFailure("Failed to copy texture to renderer");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::drawText(const std::string & text, SDL_Point position, SDL_Color textColor) const
|
|
||||||
{
|
|
||||||
SDLSurfacePtr surface(TTF_RenderUTF8_Solid(font.get(), text.data(), textColor), SDL_Surface_Deleter{});
|
|
||||||
SDLTexturePtr textTexture(SDL_CreateTextureFromSurface(renderer.get(), surface.get()), SDL_Texture_Deleter{});
|
|
||||||
auto [w, h] = textureSize(textTexture.get());
|
|
||||||
SDL_Rect textLocation = { position.x, position.y, w, h };
|
|
||||||
SDL_RenderCopy(renderer.get(), textTexture.get(), NULL, &textLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::initSDL() {
|
|
||||||
if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
|
|
||||||
exitFailure("Failed to initialize the SDL2 library");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::initSDLImage() {
|
|
||||||
int img_flags = IMG_INIT_PNG;
|
|
||||||
if (IMG_Init(img_flags) != img_flags)
|
|
||||||
exitImgFailure("Failed to init SDL_Image with png");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::initSDLTTF() {
|
|
||||||
if(TTF_Init() != 0) {
|
|
||||||
exitFailure("Unable to setup font library");
|
|
||||||
}
|
|
||||||
font.reset(TTF_OpenFont("joystix.ttf", 20));
|
|
||||||
if(!font) {
|
|
||||||
exitFailure("Failed to copy texture to renderer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::createWindow(int width, int height) {
|
|
||||||
window = std::unique_ptr<SDL_Window, SDL_Window_Deleter>(SDL_CreateWindow(
|
|
||||||
"Pacman",
|
|
||||||
SDL_WINDOWPOS_CENTERED,
|
|
||||||
SDL_WINDOWPOS_CENTERED,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI));
|
|
||||||
|
|
||||||
if (!window)
|
|
||||||
exitFailure("Failed to create window");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::createRenderer() {
|
|
||||||
renderer = std::unique_ptr<SDL_Renderer, SDL_Renderer_Deleter>(SDL_CreateRenderer(
|
|
||||||
window.get(),
|
|
||||||
-1,
|
|
||||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
|
|
||||||
|
|
||||||
if (!renderer)
|
|
||||||
exitFailure("Failed to create renderer");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::createWindowSurface() {
|
|
||||||
window_surface = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(SDL_GetWindowSurface(window.get()));
|
|
||||||
if (!window_surface)
|
|
||||||
exitFailure("Failed to get the surface from the window");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::setDrawColor() {
|
|
||||||
if (SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, SDL_ALPHA_OPAQUE) < 0)
|
|
||||||
exitFailure("Failed to set renderer color");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SDL_Texture, SDL_Texture_Deleter>
|
|
||||||
SDLWindow::loadTexture(const std::string & path) {
|
|
||||||
auto surface = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(IMG_Load(path.c_str()));
|
|
||||||
if (!surface)
|
|
||||||
exitImgFailure("Failed to load image");
|
|
||||||
|
|
||||||
auto texture = std::unique_ptr<SDL_Texture, SDL_Texture_Deleter>(
|
|
||||||
SDL_CreateTextureFromSurface(renderer.get(), surface.get()));
|
|
||||||
if (!texture)
|
|
||||||
exitFailure("Failed to create texture from surface");
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<int, int> SDLWindow::textureSize(SDL_Texture* texture) const
|
|
||||||
{
|
|
||||||
int w, h;
|
|
||||||
if (SDL_QueryTexture(texture, nullptr, nullptr, &w, &h) != 0) {
|
|
||||||
exitFailure("Failed to get texture geometry");
|
|
||||||
}
|
|
||||||
return {w, h};
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::exitFailure(const std::string & message) {
|
|
||||||
std::cerr << message << "\n";
|
|
||||||
std::cerr << "SDL2 Error: " << SDL_GetError() << "\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLWindow::exitImgFailure(const std::string & message) {
|
|
||||||
std::cerr << message << "\n";
|
|
||||||
std::cerr << "SDL2_Image Error: " << IMG_GetError() << "\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Sprite.hpp"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <SDL2/SDL_ttf.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct SDL_Window_Deleter {
|
|
||||||
void operator()(SDL_Window * window) {
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDL_Renderer_Deleter {
|
|
||||||
void operator()(SDL_Renderer * renderer) {
|
|
||||||
SDL_DestroyRenderer(renderer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDL_Surface_Deleter {
|
|
||||||
void operator()(SDL_Surface * surface) {
|
|
||||||
SDL_FreeSurface(surface);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDL_Texture_Deleter {
|
|
||||||
void operator()(SDL_Texture * texture) {
|
|
||||||
SDL_DestroyTexture(texture);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDL_Font_Deleter {
|
|
||||||
void operator()(TTF_Font* font) {
|
|
||||||
TTF_CloseFont(font);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using SDLTexturePtr = std::unique_ptr<SDL_Texture, SDL_Texture_Deleter>;
|
|
||||||
using SDLSurfacePtr = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>;
|
|
||||||
|
|
||||||
|
|
||||||
class PacMan;
|
|
||||||
class Pellets;
|
|
||||||
class SuperPellets;
|
|
||||||
|
|
||||||
class SDLWindow {
|
|
||||||
public:
|
|
||||||
explicit SDLWindow(SDL_Rect windowGeometry);
|
|
||||||
|
|
||||||
static constexpr auto White = SDL_Color{0xFF, 0xFF, 0xFF};
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void render();
|
|
||||||
|
|
||||||
Sprite getBackground() const;
|
|
||||||
Sprite getSprite(SDL_Rect rect) const;
|
|
||||||
void renderSprite(Sprite sprite, SDL_Rect target) const;
|
|
||||||
void drawText(const std::string &text, SDL_Point position, SDL_Color textColor = White) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int16_t SCALE_FACTOR = 1;
|
|
||||||
|
|
||||||
std::unique_ptr<SDL_Window, SDL_Window_Deleter> window;
|
|
||||||
std::unique_ptr<SDL_Renderer, SDL_Renderer_Deleter> renderer;
|
|
||||||
SDLSurfacePtr window_surface;
|
|
||||||
SDLTexturePtr maze_texture;
|
|
||||||
SDLTexturePtr sprite_texture;
|
|
||||||
std::unique_ptr<TTF_Font, SDL_Font_Deleter> font;
|
|
||||||
|
|
||||||
void createWindow(int width, int height);
|
|
||||||
|
|
||||||
void createRenderer();
|
|
||||||
|
|
||||||
void createWindowSurface();
|
|
||||||
|
|
||||||
static void initSDL();
|
|
||||||
|
|
||||||
static void initSDLImage();
|
|
||||||
|
|
||||||
void initSDLTTF();
|
|
||||||
|
|
||||||
void setDrawColor();
|
|
||||||
|
|
||||||
static void exitFailure(const std::string & message);
|
|
||||||
|
|
||||||
static void exitImgFailure(const std::string & message);
|
|
||||||
|
|
||||||
std::unique_ptr<SDL_Texture, SDL_Texture_Deleter>
|
|
||||||
loadTexture(const std::string & path);
|
|
||||||
|
|
||||||
SDL_Rect windowDimensions() const;
|
|
||||||
|
|
||||||
std::tuple<int, int> textureSize(SDL_Texture* texture) const;
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
#include "Sprite.hpp"
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
struct Sprite {
|
|
||||||
SDL_Texture * texture;
|
|
||||||
SDL_Rect rect;
|
|
||||||
};
|
|
|
@ -2,24 +2,21 @@
|
||||||
|
|
||||||
#include "Board.hpp"
|
#include "Board.hpp"
|
||||||
#include "Position.hpp"
|
#include "Position.hpp"
|
||||||
|
|
||||||
#include <SDL2/SDL_rect.h>
|
|
||||||
|
|
||||||
class SuperPellets {
|
class SuperPellets {
|
||||||
public:
|
public:
|
||||||
explicit SuperPellets(const Board & board);
|
explicit SuperPellets(const Board & board);
|
||||||
|
|
||||||
[[nodiscard]] SDL_Point currentSprite() const {
|
[[nodiscard]] PositionInt currentSprite() const {
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<SDL_Point> currentPositions() const {
|
[[nodiscard]] std::vector<PositionInt> currentPositions() const {
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool eatPelletAtPosition(Position p);
|
bool eatPelletAtPosition(Position p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const SDL_Point sprite = { 0, 9 };
|
const PositionInt sprite = { 0, 9 };
|
||||||
std::vector<SDL_Point> positions;
|
std::vector<PositionInt> positions;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,11 +2,6 @@ file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
|
||||||
add_executable(pacman ${sources})
|
add_executable(pacman ${sources})
|
||||||
target_link_libraries(pacman PUBLIC libpacman)
|
target_link_libraries(pacman PUBLIC libpacman)
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
find_package(sdl2 CONFIG REQUIRED)
|
|
||||||
target_link_libraries(pacman PUBLIC SDL2::SDL2main)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(pacman
|
set_target_properties(pacman
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
"name": "pacman",
|
"name": "pacman",
|
||||||
"version": "0.1",
|
"version": "0.1",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"sdl2-image",
|
"sfml",
|
||||||
"sdl2-ttf",
|
|
||||||
"fmt",
|
"fmt",
|
||||||
"zlib",
|
|
||||||
"gtest"
|
"gtest"
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue