OK, here goes.
In my game I have a vector of sf::Sprites that contains all the sprites used to draw a map, with their positions on the map already set. My current method for drawing the map just loops through all of the elements in the vector and calls draw() for each one.
This draws everything, even stuff outside of the current view, which drags the FPS down to single digits for some of my large maps.
What I'm trying to do is something like what this person has done with indexed culling.
http://en.sfml-dev.org/forums/index.php?topic=7933.0I understand how this works but the way my map is stored means I have to figure out how to find which sprite should be drawn based on where the current view is.
CreateMap.h
#pragma once
#include "SFML\Graphics.hpp"
#include <vector>
class CreateMap : public sf::Drawable, public sf::Transformable
{
public:
CreateMap(const std::string fileName, float spriteSize, int gameScale, sf::Image& spriteSheet)
{
mapScale = gameScale;
mapSpriteSize = spriteSize;
mapImage.loadFromFile(fileName);
textures.loadFromImage(spriteSheet, sf::IntRect(0, 144, 256, 48));
TILE_WIDTH = 16 * gameScale;
TILE_HEIGHT = 16 * gameScale;
MAP_WIDTH = 64 ;
MAP_HEIGHT = 64 ;
loadMap();
};
// Loads the map
void loadMap();
// Used to update the position of the view, which is used by the draw function to find out which spirtes to draw.
void indexedCulling(sf::View& view);
private:
// The map comes from an image file.
sf::Image mapImage;
// CreateMap.cpp assigns each colour found in the above image file to a particular sprite found on a spritesheet image file that has been loaded elsewhere in the program.
std::vector <sf::Color> mapPixels;
// Each sprite by now has been assigned a texture and a position on the map.
std::vector <sf::Sprite> mapSprites;
// The size of each sprite. Currently 16x16 pixels.
float mapSpriteSize;
// How big the map
int mapScale;
// This holds the image that each sprite is getting its texture from.
sf::Texture textures;
int TILE_WIDTH, TILE_HEIGHT;
int MAP_WIDTH, MAP_HEIGHT;
int startX, endX;
int startY, endY;
// This is what I want to use to draw the map
void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform *= getTransform();
for(int y = startY; y < endY; y++)
{
for(int x = startX; x < endX; x++)
{
target.draw(mapSprites[y + x]);
}
}
};
};
CreateMap.cpp
#include "CreateMap.h"
void CreateMap::loadMap()
{
int mapSize = mapImage.getSize().x * mapImage.getSize().y;
mapPixels.resize(mapSize);
// Sets the size of the vector to the size of the map image
mapSprites.resize(mapSize);
// Add new tiles here
sf::Color Grass ( 34, 177, 76, 255);
sf::Color Water ( 0, 162, 232, 255);
sf::Color Sand (255, 242, 0, 255);
sf::Color Tree (185, 122, 87, 255);
sf::Color Waves ( 63, 72, 204, 255);
sf::Color Bush (146, 195, 98, 255);
sf::Color Brown_Brick ( 53, 51, 45, 255);
sf::Color Snow (206, 214, 205, 255);
sf::Color Marble (159, 172, 149, 255);
sf::Color BLACK ( 0, 0, 0, 255);
sf::Color VOID (255, 0, 255, 255);
int i = 0;
for(int y = 0; y < mapImage.getSize().y; y++)
{
for(int x = 0; x < mapImage.getSize().x; x++)
{
mapPixels[i] = mapImage.getPixel(x, y);
mapSprites[i].setTexture(textures, false);
// Check to see what texture the pixel on the map should be
if (mapPixels[i] == Grass) mapSprites[i].setTextureRect(sf::IntRect( 64, 16, 16, 16));
else if (mapPixels[i] == Water) mapSprites[i].setTextureRect(sf::IntRect( 80, 32, 16, 16));
else if (mapPixels[i] == Waves) mapSprites[i].setTextureRect(sf::IntRect( 96, 32, 16, 16));
else if (mapPixels[i] == Sand) mapSprites[i].setTextureRect(sf::IntRect( 16, 0, 16, 16));
else if (mapPixels[i] == Tree) mapSprites[i].setTextureRect(sf::IntRect( 48, 0, 16, 16));
else if (mapPixels[i] == Bush) mapSprites[i].setTextureRect(sf::IntRect( 48, 16, 16, 16));
else if (mapPixels[i] == Brown_Brick) mapSprites[i].setTextureRect(sf::IntRect( 0, 32, 16, 16));
else if (mapPixels[i] == Snow) mapSprites[i].setTextureRect(sf::IntRect( 112, 32, 16, 16));
else if (mapPixels[i] == Marble) mapSprites[i].setTextureRect(sf::IntRect( 48, 32, 16, 16));
else if (mapPixels[i] == BLACK) mapSprites[i].setTextureRect(sf::IntRect( 64, 0, 16, 16));
// Displays pink if it can't find a texture to use
else mapSprites[i].setTextureRect(sf::IntRect(96, 0, 16, 16));
mapSprites[i].setScale(mapScale,mapScale);
mapSprites[i].setPosition(x * (mapSpriteSize * mapScale), y * (mapSpriteSize * mapScale));
i++;
}
}
}
// Should only render what is inside the view. Gets called in the game update function.
void CreateMap::indexedCulling(sf::View& view)
{
startX = (view.getCenter().x - ( 6 * TILE_WIDTH))/TILE_WIDTH;
endX = (view.getCenter().x + ( 6 * TILE_WIDTH))/TILE_WIDTH;
startY = (view.getCenter().y - ( 6 * TILE_HEIGHT))/TILE_HEIGHT;
endY = (view.getCenter().y + ( 6 * TILE_HEIGHT))/TILE_HEIGHT;
if(startX > MAP_WIDTH)
startX = MAP_WIDTH;
if(startX < 0)
startX = 0;
if(startY > MAP_HEIGHT)
startY = MAP_HEIGHT;
if(startY < 0)
startY = 0;
if(endY > MAP_HEIGHT)
endY = MAP_HEIGHT;
if(endY < 0)
endY = 0;
if(endX > MAP_WIDTH)
endX = MAP_WIDTH;
if(endX < 0)
endX = 0;
}
I'm stumped on how to get this to only render what is inside the view without having to change a lot of my existing code and would appreciate if anyone would point me in the right direction.
I know this is something I should figure out for myself, but I have been stuck on this for a few weeks and I want to move onto other things.
Sorry if my code looks messy. I'm going to clean it up when this is working.
Let me know if I need to explain anything more.