Jump to content
  • Advertisement
Sign in to follow this  
Affected1

Draw all items from std::map

This topic is 1081 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have many items in a std::map and i need to draw them all using SFML.

So far i have the following in a class called ItemManager which stores all items:

std::map<std::string, Item> items_;

"Item" is defined here:

struct Item {
	const char* name;
	ItemType type;
	sf::Sprite itemSprite;
	int x, y, sizeX, sizeY;
	int posX, posY;
	int offsetX, offsetY;
};

Now i need to send the std::map that contains all items to the main class called DGame and draw them there but i dont know how to send them there as i dont have much experience with std::map.

Share this post


Link to post
Share on other sites
Advertisement

This ^^^

 

And unless their ordering within the map is important, you probably want std::unordered_map (by default when using maps, use unordered_map unless you need to preserve insertion order).

Share this post


Link to post
Share on other sites
If you mean that you want to pass the map to the render function (and you dont have to manipulate it there) then just pass it as const reference.

Share this post


Link to post
Share on other sites

Well, std::map has begin() and end() like all other standard containers, so that is -- luckily -- not a big challenge. You can just iterate over it as usual:
 

for(auto it = items_.begin(); it != items_.end; ++it)
    do_render(*it);

or even easier:

for(auto& it : items_)
    do_render(it);

(both assume a present-day C++ version, if you use C++98 or C++03, you will have to painfully write out the iterator type, like std::map<std::string, Item>::iterator)

 

On a different note, you do not have a guarantee whatsoever that objects in a map are laid out in a cache-friendly manner in RAM, so while you can iterate a map for rendering, it is quite possible that it's not the most optimal thing to do.

Share this post


Link to post
Share on other sites

Thanks guys, it works now but i realized that if i have multiple player characters and one character is made up of multiple items, then i dont have a way to manage all the characters and move them individually on the screen. What should i do?

Share this post


Link to post
Share on other sites

Something like this:

Item::Draw(sf::RenderTarget &renderTarget, sf::Vector2f characterPos)
{
     //Position the item before drawing.
     sf::Vector2f posToDrawItem = (characterPos + itemOffset);
     sprite.setPosition(posToDrawItem);
     
     //Draw the item.
     renderTarget.draw(sprite);
}

class Character
{
    public:
    
    void Draw(sf::RenderTarget &renderTarget)
    {
         //Draw the character first.
         renderTarget.draw(characterAppearance);
         
         //Draw all the equipped items over him.
         drawEquippedStuff(renderTarget, characterAppearance.getPosition());
    }
    
     private:
     void drawEquippedStuff(sf::RenderTarget &renderTarget, sf::Vector2f characterPos)
     {
          //For every equipped item.
          for(ItemID itemID : equippedItems)
          {
               //Look up the real item using the ID.
               const Item &item = AllTheItems[ItemID];

               //Draw that item.
               item.Draw(characterPos);
          }
     }

     private:
     sf::Sprite characterAppearance;
     std::vector<ItemID> equippedItems;
};

Share this post


Link to post
Share on other sites

I managed to make a working character class but couldn't figure out how to store the items for each character.

Here is the class:

#ifndef _H_CHARACTER
#define _H_CHARACTER

#include "stdafx.h"
#include "ItemManager.h"

class Character
{
public:
	Character(std::string bodyName, int x, int y);

	void Draw(sf::RenderWindow* rw);
	void SetPosition(int x, int y);

private:
	void DrawItems(sf::RenderWindow* rw);

	ItemManager itm_mgr;
	Item body;

	int posX;
	int posY;
};

#endif

-

#include "Character.h"

Character::Character(std::string bodyName, int x, int y)
{
    body = itm_mgr.LoadItem(ITYPE_BODY, bodyName);
    posX = x;
    posY = y;
}

void Character::Draw(sf::RenderWindow* rw)
{
    body.itemSprite.setPosition(posX, posY);
    rw->draw(body.itemSprite);
}

void Character::DrawItems(sf::RenderWindow* rw)
{

}

void Character::SetPosition(int x, int y){posX = x; posY = y;}

I tried using std::map for the items but that dint work.

Share this post


Link to post
Share on other sites

If you want a single object to be shared between multiple instances of a class, you need to make it a reference or a pointer.

 

Right now you are accidentally giving each "Character" their own ItemManager. This means each separate ItemManager doesn't share the same items.

 

Further, each character doesn't even need its own items, they need to share the same items between each other.

 

It should look more like this:

struct Point
{
    int x = 0;
    int y = 0;
};

struct ItemDetails
{
    ItemType type;
    std::string imagePath;
    sf::IntRect subrect; //The subrect of the image to use for this sprite.
    Point offset; //Offset from the character origin.
};

struct Item
{
    ItemDetails details;
    sf::Sprite sprite;
    
    void Draw(sf::RenderTarget &renderTarget, Point characterPosition)
    {
        Point finalPosition = (characterPosition + details.offset);
        sprite.setPosition(float(finalPosition.x), float(finalPosition.y));
        renderTarget.draw();
    }
};

typedef unsigned int ItemID;
const ItemID InvalidItemID = ItemID(-1);

//=============================================================================

class ItemRetriever
{
public:
    
    //Called somewhere during game startup, to fill 'this->items' with all the items.
    void LoadAllItemDetails();
    
    //Retrieves an item. It's returned as a const-reference.
    //It's a reference because: We don't want to actually COPY it, only SHARE it.
    //It's const because: We don't want to MODIFY it, only READ it.
    const Item &GetItem(ItemID itemID) const
    {
        return items.at(itemID);
    }
    
private:
    std::unordered_map<ItemID, Item> items;
};

//=============================================================================

class Character
{
public:
	Character(std::string bodyName, Point position, const ItemRetriever &itemRetriever)
        : position(position), itemRetriever(itemRetriever)
    {
        //...
    }

    void SetPosition(Point position);
    
    //We use a reference, not a pointer, because references indicate that the parameter is NOT optional.
    //Because it's not optional (we can't pass nullptr or 0 or NULL), we make the code safer and less likely to crash.
    //Also, we pass a 'RenderTarget' not 'RenderWindow'. 'RenderWindow' inherits from 'RenderTarget', so this is fine,
    //but it also makes the code more flexible for if in the future you want to implement more special graphical effects.
	void Draw(sf::RenderTarget &renderTarget) const;

private:
    void drawItems(sf::RenderTarget &renderTarget) const
    {
        //Draw the body (if we have one equipped):
        if(bodyID != InvalidItemID)
        {
            const Item &body = itemRetriever.GetItem(bodyID);
            body.Draw(renderTarget, position);
        }
        
        //Draw the hat (if we have one equipped):
        if(hatID != InvalidItemID)
        {
            itemRetriever.GetItem(hatID).Draw(renderTarget, position);
        }
        
        //Draw the shoe (if we have one equipped):
        if(shoeID != InvalidItemID)
        {
            itemRetriever.GetItem(shoeID).Draw(renderTarget, position);
        }
    }

private:
    //Another const-reference, because we want to READ-ONLY SHARE this instance.
    const ItemRetriever &itemRetriever;
    
    ItemID bodyID = InvalidItemID; //If the ID is invalid, that means 'no item'.
    ItemID hatID  = InvalidItemID;
    ItemID shoesID = InvalidItemID;
    
    sf::Sprite characterAppearance;
	Point position;
};

Share this post


Link to post
Share on other sites

How can i make only one ItemManager object and then make it global so every class can access it?

I tried using extern in the stdafx.h but that dint end well..

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!