Extract Sprite into its own class, fixing memory leaks

This commit is contained in:
Reimar 2025-10-31 12:38:29 +01:00
parent 37dd9606cc
commit 623837d8ba
14 changed files with 65 additions and 70 deletions

View File

@ -4,6 +4,7 @@ OBJS= \
build/src/Game.o \
build/src/Arrow.o \
build/src/Zombo.o \
build/src/Sprite.o \
build/src/Player.o \
build/src/GameRenderer.o
@ -16,8 +17,8 @@ RELEASE=0
ifeq ($(RELEASE), 1)
CXX_FLAGS += -Werror
else
CXX_FLAGS += -fsanitize=address,undefined
LINKER_FLAGS += -fsanitize=address,undefined
CXX_FLAGS += -fsanitize=address,undefined -g -Og -rdynamic
LINKER_FLAGS += -fsanitize=address,undefined -g -Og -rdynamic
endif
CXX_FLAGS += $(shell pkg-config sdl2 SDL2_image --cflags)

View File

@ -1,5 +1,6 @@
#include <cmath>
#include <numbers>
#include <memory>
#include "Arrow.hpp"
Arrow::Arrow(GameRenderer *renderer, const double x, const double y, const double angle)
@ -8,17 +9,12 @@ Arrow::Arrow(GameRenderer *renderer, const double x, const double y, const doubl
sprite = renderer->load_sprite("./assets/arrow.png", 22, 8);
}
Arrow::~Arrow()
{
renderer->unload_sprite(sprite);
}
void Arrow::draw(const double player_x, const double player_y) const
{
renderer->draw_sprite_rotated(
sprite,
(int)(x - sprite.width / 2 + renderer->screen_width / 2 - player_x),
(int)(y - sprite.height / 2 + renderer->screen_height / 2 - player_y),
*sprite,
(int)(x - sprite->width / 2 + renderer->screen_width / 2 - player_x),
(int)(y - sprite->height / 2 + renderer->screen_height / 2 - player_y),
angle * 180 / std::numbers::pi
);
}

View File

@ -1,6 +1,7 @@
#ifndef ARROW_HPP
#define ARROW_HPP
#include <memory>
#include "GameRenderer.hpp"
#include "Sprite.hpp"
@ -8,7 +9,7 @@ class Arrow
{
private:
GameRenderer *renderer;
Sprite sprite;
std::unique_ptr<Sprite> sprite;
double x;
double y;
@ -19,8 +20,6 @@ private:
public:
Arrow(GameRenderer *renderer, double x, double y, double angle);
~Arrow();
void draw(double offset_x, double offset_y) const;
void update();

View File

@ -24,7 +24,7 @@ void Game::update(std::stop_token stop_token)
}
if (ticks % 100 == 0 && zombos.size() < 20) {
zombos.push_back(Zombo(&renderer, 20.0, 20.0));
zombos.emplace_back(&renderer, 20.0, 20.0);
}
renderer.redraw();

View File

@ -1,4 +1,5 @@
#include <SDL2/SDL_image.h>
#include <memory>
#include <iostream>
#include "Sprite.hpp"
#include "GameRenderer.hpp"
@ -39,27 +40,18 @@ GameRenderer::~GameRenderer()
SDL_DestroyWindow(window);
}
Sprite GameRenderer::load_sprite(const std::string &file, const int width, const int height) const
std::unique_ptr<Sprite> GameRenderer::load_sprite(const std::string &file, const int width, const int height) const
{
return Sprite {
.texture = IMG_LoadTexture(renderer, file.c_str()),
.width = width,
.height = height
};
return std::make_unique<Sprite>(renderer, file, width, height);
}
void GameRenderer::unload_sprite(const Sprite &sprite)
{
SDL_DestroyTexture(sprite.texture);
}
void GameRenderer::draw_sprite(const Sprite sprite, const int x, const int y) const
void GameRenderer::draw_sprite(const Sprite &sprite, const int x, const int y) const
{
const SDL_Rect rect = { .x = x, .y = y, .w = sprite.width, .h = sprite.height };
SDL_RenderCopy(renderer, sprite.texture, nullptr, &rect);
}
void GameRenderer::draw_sprite_rotated(const Sprite sprite, const int x, const int y, const double angle) const
void GameRenderer::draw_sprite_rotated(const Sprite &sprite, const int x, const int y, const double angle) const
{
const SDL_Rect rect = { .x = x, .y = y, .w = sprite.width, .h = sprite.height };
SDL_RenderCopyEx(renderer, sprite.texture, nullptr, &rect, angle, nullptr, SDL_FLIP_NONE);

View File

@ -1,6 +1,7 @@
#ifndef GAME_RENDERER_HPP
#define GAME_RENDERER_HPP
#include <memory>
#include <string>
#include <SDL2/SDL.h>
#include "Sprite.hpp"
@ -21,13 +22,11 @@ public:
~GameRenderer();
Sprite load_sprite(const std::string &file, int width, int height) const;
std::unique_ptr<Sprite> load_sprite(const std::string &file, int width, int height) const;
void unload_sprite(const Sprite &sprite);
void draw_sprite(const Sprite &sprite, int x, int y) const;
void draw_sprite(Sprite sprite, int x, int y) const;
void draw_sprite_rotated(Sprite sprite, int x, int y, double angle) const;
void draw_sprite_rotated(const Sprite &sprite, int x, int y, double angle) const;
void clear_screen(uint8_t r, uint8_t g, uint8_t b, uint8_t a) const;

View File

@ -28,12 +28,6 @@ Map::Map(GameRenderer *renderer, int tile_size) :
}
}
Map::~Map()
{
renderer->unload_sprite(grass_sprite);
renderer->unload_sprite(path_sprite);
}
std::vector<Tile> Map::generate_tiles(const std::vector<Tile> &prev_tiles) const
{
std::vector<Tile> new_tiles = std::vector(prev_tiles.size(), Tile::grass);
@ -109,8 +103,8 @@ void Map::draw(const double player_x, const double player_y) const
}
switch (tiles[tile_x][tile_y]) {
case Tile::grass: renderer->draw_sprite(grass_sprite, screen_x, screen_y); break;
case Tile::path: renderer->draw_sprite(path_sprite, screen_x, screen_y); break;
case Tile::grass: renderer->draw_sprite(*grass_sprite, screen_x, screen_y); break;
case Tile::path: renderer->draw_sprite(*path_sprite, screen_x, screen_y); break;
}
}
}

View File

@ -2,6 +2,7 @@
#define MAP_HPP
#include <vector>
#include <memory>
#include "Tile.hpp"
#include "Sprite.hpp"
#include "GameRenderer.hpp"
@ -12,8 +13,7 @@ private:
GameRenderer *renderer;
std::vector<std::vector<Tile>> tiles;
Sprite grass_sprite;
Sprite path_sprite;
std::unique_ptr<Sprite> grass_sprite, path_sprite;
[[nodiscard]] std::vector<Tile> generate_tiles(const std::vector<Tile> &prev_tiles) const;
@ -24,7 +24,7 @@ public:
Map(GameRenderer *renderer, int tile_size);
~Map();
~Map() = default;
void draw(double player_x, double player_y) const;

View File

@ -8,24 +8,18 @@ Player::Player(GameRenderer *renderer) : renderer(renderer)
bow_sprite = renderer->load_sprite("./assets/bow_arrow.png", 22, 32);
}
Player::~Player()
{
renderer->unload_sprite(hero_sprite);
renderer->unload_sprite(bow_sprite);
}
void Player::draw() const
{
renderer->draw_sprite(
hero_sprite,
renderer->screen_width / 2 - hero_sprite.width / 2,
renderer->screen_height / 2 - hero_sprite.height / 2
*hero_sprite,
renderer->screen_width / 2 - hero_sprite->width / 2,
renderer->screen_height / 2 - hero_sprite->height / 2
);
renderer->draw_sprite_rotated(
bow_sprite,
(int)(renderer->screen_width / 2 - bow_sprite.width / 2 + std::cos(angle) * 30),
(int)(renderer->screen_height / 2 - bow_sprite.height / 2 + std::sin(angle) * 30),
*bow_sprite,
(int)(renderer->screen_width / 2 - bow_sprite->width / 2 + std::cos(angle) * 30),
(int)(renderer->screen_height / 2 - bow_sprite->height / 2 + std::sin(angle) * 30),
angle * 180 / std::numbers::pi
);
}

View File

@ -1,13 +1,14 @@
#ifndef PLAYER_HPP
#define PLAYER_HPP
#include <memory>
#include "GameRenderer.hpp"
class Player
{
private:
GameRenderer *renderer;
Sprite hero_sprite, bow_sprite;
std::unique_ptr<Sprite> hero_sprite, bow_sprite;
public:
double x = 0;
@ -22,7 +23,7 @@ public:
Player(GameRenderer *renderer);
~Player();
~Player() = default;
void draw() const;

14
src/Sprite.cpp Normal file
View File

@ -0,0 +1,14 @@
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_image.h>
#include "Sprite.hpp"
#include <iostream>
Sprite::Sprite(SDL_Renderer *renderer, const std::string &file, int width, int height) : width(width), height(height)
{
texture = IMG_LoadTexture(renderer, file.c_str());
}
Sprite::~Sprite()
{
SDL_DestroyTexture(texture);
}

View File

@ -2,11 +2,22 @@
#define SPRITE_HPP
#include <SDL2/SDL_render.h>
#include <string>
typedef struct {
class Sprite {
public:
SDL_Texture *texture;
int width;
int height;
} Sprite;
Sprite(SDL_Renderer *renderer, const std::string &file, int width, int height);
~Sprite();
Sprite(const Sprite&) = delete;
Sprite operator=(const Sprite&) = delete;
Sprite(Sprite&&) = delete;
Sprite operator=(Sprite&&) = delete;
};
#endif

View File

@ -5,11 +5,6 @@ Zombo::Zombo(GameRenderer *renderer, const double x, const double y) : renderer(
sprite = renderer->load_sprite("./assets/zombo.png", 40, 40);
}
Zombo::~Zombo()
{
renderer->unload_sprite(sprite);
}
void Zombo::update(const double player_x, const double player_y)
{
double angle = std::atan2(player_y - y, player_x - x);
@ -21,8 +16,8 @@ void Zombo::update(const double player_x, const double player_y)
void Zombo::draw(const double player_x, const double player_y) const
{
renderer->draw_sprite(
sprite,
(int)(x - sprite.width / 2.0 + renderer->screen_width / 2.0 - player_x),
(int)(y - sprite.height / 2.0 + renderer->screen_height / 2.0 - player_y)
*sprite,
(int)(x - sprite->width / 2.0 + renderer->screen_width / 2.0 - player_x),
(int)(y - sprite->height / 2.0 + renderer->screen_height / 2.0 - player_y)
);
}

View File

@ -1,6 +1,7 @@
#ifndef ZOMBO_HPP
#define ZOMBO_HPP
#include <memory>
#include "Sprite.hpp"
#include "GameRenderer.hpp"
@ -8,7 +9,7 @@ class Zombo
{
private:
GameRenderer *renderer;
Sprite sprite;
std::unique_ptr<Sprite> sprite;
double x;
double y;
@ -18,8 +19,6 @@ private:
public:
Zombo(GameRenderer *renderer, double x, double y);
~Zombo();
void update(double player_x, double player_y);
void draw(double player_x, double player_y) const;