Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: [Solved] How to have a std::vector of sf::Drawable items and draw them?  (Read 13416 times)

0 Members and 1 Guest are viewing this topic.

wh1t3crayon

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
I'm working on a grid to speed up collision detection, and the way I have this set up is that a GridSquare class holds a std::vector of all the objects that are within that specific grid square (determined by using a sf::FloatRect::intersects()). Here is what I mean by this class:
class GridSquare{
public:
        GridSquare(float x, float y, float px, float py); //position.x, position.y, width, height,
        ~GridSquare();

        sf::RectangleShape &GetVisual();
        sf::FloatRect &GetGridSquare();

        void AddStationaryObject(sf::Transformable &object);
        void AddMovableObject(sf::Transformable &object);

        std::vector<sf::Transformable> &GetStationaryObjects();
        std::vector<sf::Transformable> &GetMovableObjects();
        std::vector<sf::Transformable> &GetGridObjects();
private:
//so in the gameloop I'll check for what objects intersect this float rect, and...
        sf::FloatRect gridSquare;
//...add these objects to the respective vectors below
        //stationary objects hold walls and such, movable objects would be player and projectiles
        std::vector<sf::Transformable> stationaryObjects_, movableObjects_, gridObjects_;
};

The issue is that sf::RenderWindow::draw() does not take a sf::Transformable as a parameter, so when I declare the vectors above as "std::vector<sf::Transformable>" and try to loop through the vector and draw the objects, this compiler error is thrown:
Quote
IntelliSense: no instance of overloaded function "sf::RenderWindow::draw" matches the argument list
            argument types are: (sf::Transformable)
            object type is: sf::RenderWindow   c:\Users\jordan\Documents\Visual Studio 2012\Projects\SFML Test\SFML Test\SFML Test.cpp   20
Here is a small code example to see for yourself:
int _tmain(int argc, _TCHAR* argv[]){
        sf::RenderWindow window;
        window.create(sf::VideoMode(500, 500, 32), "Test");
        //a vector of sf::Transformable, can't be drawn on screen because a transformable isn't a drawable
        std::vector<sf::Transformable> objectsToDraw;
        objectsToDraw.push_back(sf::RectangleShape(sf::Vector2f(20, 20)));

        while(window.isOpen()){
                //just calling the draw function here, no need to worry about updating because nothing is being updated
                window.clear();
                window.draw(objectsToDraw[0]); //throws an error that draw() doesn't take an argument of sf::Transformable
                window.display();
        }

        return 0;
}

So, the ideal fix would be to declare those std::vectors as std::vector<sf::Drawable>, that way their objects can be drawn, right? Well, that throws a different error:
Quote
Error   2   error C2259: 'sf::Drawable' : cannot instantiate abstract class   c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0   617

Again, a small code example:
#include "stdafx.h"
#include "SFML\Graphics.hpp"

int _tmain(int argc, _TCHAR* argv[]){
        sf::RenderWindow window;
        window.create(sf::VideoMode(500, 500, 32), "Test");
        //a vector of sf::Drawable, throws an error that the abstract class can't be instantiated
        std::vector<sf::Drawable> objectsToDraw;
        objectsToDraw.push_back(sf::RectangleShape(sf::Vector2f(20, 20)));

        while(window.isOpen()){
                //just calling the draw function here, no need to worry about updating because nothing is being updated
                window.clear();
                //window.draw(objectsToDraw[0]); //the error isn't thrown here, but above when the vector is declared
                window.display();
        }

        return 0;
}

I don't understand this error. I just want a std::vector that will hold all types of sfml drawable objects, which I can then iterate through and draw all the objects on screen. Any suggestions?
« Last Edit: January 17, 2015, 09:52:42 pm by wh1t3crayon »

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
This quite basic C++ knowledge. sf::Drawable is abstract (Meaning it cannot be directly instanciated, only inherited from), therefore you cannot have a container of it (Or a class/struct member etc). You can however, have a pointer (Smart or raw) to sf::Drawable and it will work.

This will not solve your problem however. sf::Transformable has positional data which I'm guessing you need for the position of your grid cells, but how are you supposed to draw a position (Not to mention sf::Window::draw takes an sf::Drawable not sf::Transforamble)?

My advice is to make your own class inherited from both sf::Transformable and sf::Drawable, that way you can draw the instances AND transform them.

As for looping, there are a few ways of doing them:

Pre C++11:
std::vector<MyDrawableTransformable> x;
...
std::vector<MyDrawableTransformable>::iterator it;
for (it = x.begin(); it != x.end(); ++it)
   window.draw(**it);
 

C++11/14
std::vector<MyDrawableTransformable> x;
...
for (auto obj : x)
   window.draw(obj);
 

I didnt test the code (Plus its pseudo code anyway) so theres probably something I missed but thats the idea.

fallahn

  • Sr. Member
  • ****
  • Posts: 492
  • Buns.
    • View Profile
    • Trederia
You'll probably want
for(auto& obj : x)

Or even
for(const auto& obj : x)

Else obj will be a copy of whatever is in your container, which you probably don't want from a drawable/transformable (or can't if you also inherit NonCopyable)

wh1t3crayon

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Thanks for the replies, but I know how to do the actual for loop, the title may have been a bit misleading. As for creating my own class inhertied from sf::Transformable and sf::Drawable, that is new to me. I know I would declare the class like so:
class MyDrawableTransformable : sf::Transformable, sf::Drawable{
//...
};
But the question is how would that change how I declare my std::vectors? When Gambit typed
Quote
std::vector<MyDrawableTransformable> x;
...
for (auto obj : x)
   window.draw(obj);
I assume that means that I would be looping through a vector of class objects and drawing the sf::Drawables owned by those class objects instead of directly looping through sfml objects?

So let's compare that to my GridSquare class that I gave earlier:
class GridSquare{
//...
    //stationary objects hold walls and such, movable objects would be player and projectiles
//have each GridSquare object own a vector of class objects, and all my objects I want to draw, such as walls, projectiles, etc, inherit from MyDrawableTransformable?
    std::vector<sf::MyDrawableTransformable> stationaryObjects_, movableObjects_, gridObjects_;
};

So does MyDrawbleTransformable hold all of my world objects, or do all the objects I want to draw inherit from MyDrawableTransformable?

Edit: a little bit of research and that was answered, all my object classes need to inherit from MyDrawableTransformable. The question I have now is what does the class MyDrawableTransformable need to look like?
« Last Edit: January 17, 2015, 07:20:36 pm by wh1t3crayon »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
It's hard to tell exactly which questions are or aren't being asked at this point, but whatever's left seems to be about basic C++ polymorphism works.

Quote
The question I have now is what does the class MyDrawableTransformable need to look like?

As with any other container of polymorphic objects, the container should be of base class pointers, and the base class should (in most cases) be an abstract class containing only pure virtual methods that define the interface you need when dealing with unknown derived types.  Presumably, the interface you want for MyDrawableTransformable is a combination of the sf::Drawable and sf::Transformable interfaces, so MyDrawableTransformable could simply be a class that inherits from those two and has nothing in it.

wh1t3crayon

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Quote
Presumably, the interface you want for MyDrawableTransformable is a combination of the sf::Drawable and sf::Transformable interfaces, so MyDrawableTransformable could simply be a class that inherits from those two and has nothing in it.
But wouldn't MyDrawableTransformable at least have to implement draw(), because that is a virtual method from sf::Drawable?

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
To be more precise, it is a pure virtual function which means it HAS to be implemented in derived classes and makes the owning class (sf::Drawable) abstract. I'm pretty sure I covered this in my original response.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
The concrete, derived types do have to implement draw().  The abstract base class does not.  See http://ideone.com/ewU9qP for an example.

An abstract base class inheriting from other abstract base classes won't change this in any way.
« Last Edit: January 17, 2015, 07:52:29 pm by Ixrec »

wh1t3crayon

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Okay, so I'm undertsnading that the code would look something like this:
#include "SFML\Graphics.hpp"

class MyDrawableTransformable : sf::Drawable, sf::Transformable{
public:
        MyDrawableTransformable() {};
        ~MyDrawableTransformable() {};
};

class Player : MyDrawableTransformable {
        //...
        sf::CircleShape circle;
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
            states.transform *= getTransform(); /*error: getTransform() is inaccessible*/
            target.draw(circle, states);
        }
}

//I leave out class GridSquare because it doesn't have to be drawn, it is just a bunch of
//sf::FloatRects making up the grid. Instead, class GridSquare would hold a vector of objects that
//inherit from MyDrawableTransformable and are inside its sf:FloatRect
 
But like the comment says, getTransform() is inaccessible. Am I implementing draw() incorrectly?

Hapax

  • Hero Member
  • *****
  • Posts: 3350
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
I know I would declare the class like so:
class MyDrawableTransformable : sf::Transformable, sf::Drawable{
//...
};
You also need the public keyword for each base as shown here in this tutorial.
In that tutorial, it also shows examples of how to implement the draw method.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: How do you loop through a std::vector of sf::Drawable items and draw them?
« Reply #10 on: January 17, 2015, 08:13:32 pm »
You could have very easily googled that error message.  Here's one good answer: https://stackoverflow.com/questions/5844584/error-function-is-inaccessible

tl;dr you forgot to write "public" when specifying the base class.


In all seriousness, if you're still getting stuck then you need to go read a C++ book or tutorial about how class inheritance and polymorphism works.  Everything in this thread so far is need-to-know material for working with C++.

wh1t3crayon

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Re: How do you loop through a std::vector of sf::Drawable items and draw them?
« Reply #11 on: January 17, 2015, 09:51:47 pm »
Thanks for the help. And while the book would be useful, nothing works as well as hands on debugging, at least in my experience. I've learned more about polymorphism from this post than I did when learning c++  :P

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10815
    • View Profile
    • development blog
    • Email
Re: [Solved] How to have a std::vector of sf::Drawable items and draw them?
« Reply #12 on: January 17, 2015, 10:03:14 pm »
It's true that doing things practically usually teach more than just reading about it in theory, however keep in mind that for this thread you've had 5 different people trying to explain you something, while a good C++ book had about the same information within and most of the good C++ books have practical examples to exercise what is being taught. As such, if one does the provided exercise of a good C++ book and uses said book as reference point when needed, it's possible to figure things out on your own, without allocating the free time of other people. ;)

I don't want to say that these people wouldn't want to spend their free time helping, but more to show that good C++ books are more valuable than you might think.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hapax

  • Hero Member
  • *****
  • Posts: 3350
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How do you loop through a std::vector of sf::Drawable items and draw them?
« Reply #13 on: January 17, 2015, 11:59:03 pm »
you forgot to write "public" when specifying the base class.
I think I just said that  8)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Skorpion

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: [Solved] How to have a std::vector of sf::Drawable items and draw them?
« Reply #14 on: October 15, 2015, 08:33:19 pm »
So I'm having a problem doing something like this. I made a MyDrawableTransformable kind class but I have to override sf::Drawable::draw method and I have no idea how to do that to not change what those method already do. Can you give me some advice, hov should I write it?

Sorry for my english.