I am trying to make an "resource manager" for my 2D game with c++.
The initial idea is anything like this:
class resource_manager
{
private:
static std::unordered_map<std::string, std::shared_ptr<texture> > all_textures;
public:
static std::shared_ptr<texture> texture_load(std::string path)
{
// if path no exist in map all_textures:
// load texture, store in map, return the pointer.
// else: only return the pointer.
}
protected
resource_manager() {}
~resource_manager() {}
};
class texture
{
private:
unsigned id; // opengl texture id
unsigned width;
unsigned height;
public:
texture(unsigned _id, unsigned _width, unsigned _height)
: id(_id), width(_width), height(_height) {}
~texture() {}
// getters and setters
};
But, i have a problem. When the shared_ptr counter come to < 0, the pointer of texture will be destroyed, ok, but my texture still continues in VGA memory. So, when the pointer destruct herself, i need call glDeleteTextures() to unload the texture in memory.
C++ is not my speciality, for that, is hard to me do it.
Sorry for my poor english. Any help is welcome.
Related
here is the structure of my program:
class Game
{
public:
unsigned int progressFlags;
sf::RenderWindow appWindow;
Screen* currentScreen;
Game(const unsigned int& width, const unsigned int& height,
const unsigned int& bitsPerPixel, const std::string& windowName);
~Game();
void drawScreen();
};
Game contains a Screen which is defined as:
class Screen
{
public:
std::vector<sf::Drawable*> sceneElements;
sf::Sprite backgroundSprite;
Screen();
virtual ~Screen();
protected:
sf::Texture backgroundTexture;
};
Elements of std::vector sceneElements are collected from screens like this:
sceneElements.push_back(&backgroundSprite);
At last, I am trying to draw all my graphic objects using a simple method:
void Game::drawScreen()
{
for (unsigned int i = 0; i < currentScreen->sceneElements.size(); i++)
appWindow.draw(*currentScreen->sceneElements(i));
}
But appWindow.draw() fails. I think it is something about dereferencing pointers (some basic c++ knowledge I cant just remember), but I don't know how to fix it. I would appreciate any help.
I'd just derive your custom classes from sf::Drawable (and potentially sf::Transformable). This way drawing hierarchically is very easy and basically done by SFML. The following snippets are written from memory; there might be typos. :)
You'll just have to subclass your Screen class:
class Screen : public sf::Drawable
{
public:
std::vector<sf::Drawable*> sceneElements;
sf::Sprite backgroundSprite;
Screen();
virtual ~Screen();
virtual void draw(sf::RenderTarget &rt, sf::RenderStates states) const;
protected:
sf::Texture backgroundTexture;
};
Inside draw() you'd just iterate over all your elements (similar to the way you do already):
void draw(sf::RenderTarget &rt, sf::RenderStates states) const
{
for (const auto &e : sceneElements)
rt.draw(*e, states);
}
Here we're dereferencing once to get from a pointer back to the object, which is then passed by reference (where inheritance does the rest).
Game::drawScreen() becomes more simple as well:
void Game::drawScreen()
{
appWindow.draw(*currentScreen);
}
Once again, derefrencing once to go from the pointer to the actual object (passed by reference).
Also note that your original design violates encapuslation, by Game having to know how to work with stuff that's inside your Scene. So if you change things inside Scene, you'll have to adjust Game accordingly. Using inheritance, you're avoiding this issue.
I have a class like so:
Texture
{
int ID
public:
Texture(std::string name){ ID = make_texture(name); }
~Texture(){ delete_texture(ID); }
};
but the problem is that when I move the class, the destructor is called so the ID is now invalid.
my current implementation would be something like:
Texture
{
static std::unordered_map<int> m;
int ID
public:
Texture(std::string name){
ID = make_texture(name);
m[ID]++;
}
Texture(Texture& obj){ *this = obj; }
Texture &operator=(Texture& obj){
ID = obj.ID;
m[ID]++;
}
~Texture(){
if (!--m[ID])
delete_texture(ID);
}
};
//was coded in stack overflow so syntax may be a bit off
but what would really be nice is a class I could inherit from like:
Texture : public ref_count<int>
{
int ID
public:
Texture(std::string name){ ID = make_texture(name); }
key(){return ID;} // inherited from ref_count
on_delete(){ delete_texture(ID); } // inherited from ref_count
};
so my question is: does a convenient class like this exist in the standard / boost library? Or what is the best way to achieve this without implementing my own reference counting.
To expand on my comment. You need Texture objects to be shared references to the same ID, so it's ID that needs to be wrapped in some reference counting type for Texture to hold. That's exactly a use case for std::shared_ptr. All you need is a custom deleter that would delete_texture as part of freeing the managed integer.
class Texture
{
std::shared_ptr<int> ID;
public:
Texture(std::string name) :
ID{ new int(make_texture(name)),
[](int* id_ptr) {
delete_texture(*id_ptr);
delete id_ptr;
}
}
{}
};
And that's it. The copy/move/dtor of Texture can now be implicitly generated by the compiler, since it relies on the correct behavior of std::shared_ptr.
To be more specific, I have a class that looks like this:
class Ball {
public:
unsigned collider_size;
scionofbytes::MovementComponent movement;
scionofbytes::GraphicComponent graphic;
Ball(u_int init_collider_size, std::string texture_path) {
collider_size = init_collider_size;
movement = scionofbytes::MovementComponent();
graphic = scionofbytes::GraphicComponent(
(u_int) collider_size/2,
(u_int) collider_size/2,
texture_path
);
}
};
I'm accepting the texture_path and passing it on to the graphic component, which looks like this:
class GraphicComponent {
unsigned height;
unsigned width;
public:
sf::Texture texture;
sf::Sprite sprite;
GraphicComponent() {}
GraphicComponent(unsigned init_height, unsigned init_width, std::string texture_path) {
width = init_width;
height = init_height;
texture.loadFromFile(texture_path);
sprite.setTexture(texture);
}
};
When I instantiate a ball object by passing in the texture_path, I'm creating a texture as a member of the graphic component and then assigning that texture to the graphic component's sprite member.
When using this sprite member to draw to the screen, I'm facing SFML's known white box problem.
Now from my understanding, of the ball objects stays alive, the graphic component member also stays alive as does the texture member of the graphic component.
So my question is, why does this not work? When using the sprite to draw on screen, I still get a white box. Why is the texture getting destroyed?
In your Ball class constructor you are making a copy of your GraphicComponent. IIRC sf::Sprite only holds a reference to the sf::Texture so your copy may end up with the sf::Sptite pointing to the deleted sf::Texture from the object it got copied from.
Try constructing your Ball without making a copy of your GraphicComponent:
class Ball {
public:
unsigned collider_size;
scionofbytes::MovementComponent movement;
scionofbytes::GraphicComponent graphic;
// Use initializer-list
Ball(u_int init_collider_size, std::string texture_path)
: collider_size(init_collider_size)
, movement()
, graphic((u_int) collider_size/2, (u_int) collider_size/2, texture_path)
{
// don't assign stuff here if you can avoid it
}
};
In addition to that you may also want to create a copy constructor for your GraphicComponent class to prevent corruption elsewhere:
class GraphicComponent
{
unsigned height;
unsigned width;
public:
sf::Texture texture;
sf::Sprite sprite;
GraphicComponent()
{
}
GraphicComponent(unsigned init_height, unsigned init_width,
std::string texture_path)
{
width = init_width;
height = init_height;
texture.loadFromFile(texture_path);
sprite.setTexture(texture);
}
// Give it a copy constructor
GraphicComponent(GraphicComponent const& other)
: height(other.height)
, width(other.width)
, texture(other.texture)
, sprite(texture) // make it point to the new Texture not the other one
{
}
};
I am writing a C++ program that reads a .bmp file and creates a dynamic 2D array of pixels[1], that represents the image. That information is stored in a class Image[2] instance.
So, I want to have functions that apply some color effects to the image, but I want only those functions to be able to access Image's private variables.
The Image class must be able to work without the color effects, so they cannot be functions of the class (inline), but the Image class, also do not have any getters nor setters. I thought of a friend functions, but that means that I must list each of those functions manually. I would be really grateful if someone could help me with my problem!
[1]
struct Pixel
{
unsigned char Red;
unsigned char Blue;
unsigned char Green;
unsigned char Unused;
};
[2]
class Image
{
public:
Image();
~Image();
bool Open(char*);
void Close();
bool Save();
private:
bool good;
Pixel** loadedImage;
char* filePath;
};
It would seem that the most sensible way of doing it would be to make a class that has static members, and then make this class a friend class of Image. So, for e.g,
class Bitmap {
...
friend class ImageHandler;
}
class ImageHandler {
static void Manipulate();
}
void ImageHandler::Manipulate() {
// now you can access all of the private vars of Pixel.
}
void Pixel::SomeFunction() {
ImageHandler::Manipulate();
}
How can I refractor the following, to move my drawing functions from the h-file into a GraphicsManager class?
//drawingFunctions.h
void drawTexturedQuad( Texture texture, Vector2 pos, Vector2 dim) {
// bind texture...
glBegin(...); // draw
//...
glEnd(...);
}
//class file
#include "drawingFunctions.h"
class Player {
void drawPlayer(){ drawTexturedQuad( texture, pos, dim) }
};
class Enemy {
void drawEnemy(){ drawTexturedQuad( texture, pos, dim) }
};
class Item {
void drawItem(){ drawTexturedQuad( texture, pos, dim) }
};
// and so on for the other components
//gameloop file
// instantiate components objects
while (true) {
// input, logic
Player.drawPlayer();
Enemy.drawEnemy();
Item.drawItem();
// and so on
}
(The code is obviously simplified, I am just asking about the drawing here)
Should I...
pass a pointer to the GraphicsManager to every call of drawPlayer, drawEnemy etc from within the gameloop
have Player, Enemy etc have a pointer to GraphicsManger as a data member
have Player, Enemy etc extend a drawableGameComponent class that has a pointer to GraphicsManager as a data member
something else?
That sounds like a perfect use case for inheritance:
class Drawable
{
public:
void draw()
{
// gl stuff
}
protected:
Texture _texture;
Vector2 _pos;
Vector2 _dim;
};
class Player : Drawable
{
public:
// should modify _texture _pos and _dim somewhere.
};
// same thing for the other objects.
I would pass a renderer to the model, and ask it to draw itself.
class Player
{
public:
void draw(Renderer& renderer);
};
class Enemy
{
public:
void draw(Renderer& renderer);
};
Note you don't have to name the function drawPlayer or drawEnemy, because you already know that it's a Player or an Enemy by the class type. This uniform calling convention is perfect for extracting into a common interface:
class Model
{
public:
virtual void draw(Renderer& renderer) = 0;
virtual ~Model() {}
};
Then you can have each of your models inherit from Model, and each implement draw.
As I mentioned in a comment on #J.N.'s answer you can also have Renderer be an abstract class. For example I worked on a project which used OpenGL, GDI+, and also needed to create printouts of schematics.
class Renderer
{
public:
virtual render(const Triangle& triangle, const Texture& texture) = 0;
virtual ~Renderer() {}
};
I would go for the first possibility: passing the pointer to the GraphicsManager in the call. Eventhough it seems a bit overkill, the knowledge of which GraphicsManager is used is maintained on higher level and can be modified easier later on.
Having said this, I would still inherit from a Drawable interface and put the items that need to be drawn in a container, so you can just iterate through it to display the items via a virtual drawItem() function.
Like so (C++03, not tested):
std::vector<Drawable*> Items;
Items.push_back(&player);
Items.push_back(&enemy);
...
for (std::vector<Drawable*>::iterator it = Items.begin(); it != Items.end(): ++it)
{
(*it)->drawItem(&graphMgr);
}