Hello, I've been trying to implement a culling algorithm in my game, but instead of only calling draw() when the tiles are in view, it draws everything untill the camera starts moving (side-scroller camera). After that, it just stops drawing everything.
Here's my best attempt so far:
sf::View cam = target.getView();
sf::FloatRect screenRect(sf::Vector2f(
cam.getCenter().x - (cam.getSize().x/2.f),
cam.getCenter().y - (cam.getSize().y/2.f)),
cam.getSize());
for (int i = 0; i < m_map.size(); i++)
{
for (int j = 0; j < m_map[i].size(); j++)
{
if (m_map[i][j].x != -1 && m_map[i][j].y != -1 && screenRect.intersects(sf::FloatRect(m_map[i][j].x * 32, m_map[i][j].y * 32, 32, 32)))
{
m_tilesprite.setPosition(j * tileSize, i * tileSize);
m_tilesprite.setTextureRect(sf::IntRect(m_map[i][j].x * tileSize, m_map[i][j].y * tileSize, tileSize, tileSize));
target.draw(m_tilesprite);
}
}
}
m_map is a 2d vector of sf::Vector2i that contains coordinates for tiles in the current level.
What am I doing wrong?
Thanks for the reply. I'll address all of your questions in order:
- This code is called after the camera movement has been processed.
- The call is made after clearing the screen and before drawing the player sprite.
- No, the code from the previous post is only called once each frame.
- m_tilesprite is a sf::Sprite that represents the tiles, defined by what the current tile to draw is.
Here's the main function:
#include <SFML/Graphics.hpp>
#include "Player.hpp"
#include "WorldMap.hpp"
int main()
{
sf::RenderWindow wnd(sf::VideoMode(1280, 720), "Test");
sf::View cam(wnd.getDefaultView());
WorldMap map;
map.loadMap("resources\\maps\\map.txt");
Player player(200.f);
player.setPosition(1280.f / 4.f, 720.f/ 2.f);
while (wnd.isOpen())
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) && !sf::Keyboard::isKeyPressed(sf::Keyboard::D))
player.run(Player::direction::left);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) && !sf::Keyboard::isKeyPressed(sf::Keyboard::A))
player.run(Player::direction::right);
if (player.getPosition().x + player.rect.getSize().x > screenDimensions.x / 2)
{
if (player.getRight())
cam.setCenter(player.getPosition().x + (player.rect.getSize().x / 2), player.getPosition().y);
else
cam.setCenter(player.getPosition().x - (player.rect.getSize().x / 2), player.getPosition().y);
wnd.setView(cam);
}
wnd.clear();
map.Draw(wnd); //this is the call to what i originally posted.
wnd.draw(player.sprite);
wnd.display();
}
return 0;
}
Here's a cleaner version of the Draw call:
sf::View cam = target.getView();
sf::FloatRect screenRect(cam.getCenter() - (cam.getSize() / 2.f), cam.getSize());
for (int i = 0; i < m_map.size(); i++)
{
for (int j = 0; j < m_map[i].size(); j++)
{
if (m_map[i][j].x != -1 && m_map[i][j].y != -1 && screenRect.intersects(sf::FloatRect(m_map[i][j].x * tileSize, m_map[i][j].y * tileSize, tileSize, tileSize)))
{
m_tilesprite.setPosition(j * tileSize, i * tileSize);
m_tilesprite.setTextureRect(sf::IntRect(m_map[i][j].x * tileSize, m_map[i][j].y * tileSize, tileSize, tileSize));
target.draw(m_tilesprite);
}
}
}
tileSize is 32.
And here's the loadMap function:
void WorldMap::loadMap(const char* filename)
{
std::ifstream file(filename);
m_tempmap.clear();
m_map.clear();
if (file.is_open())
{
std::string tileLocation;
file >> tileLocation;
m_tiletexture.loadFromFile(tileLocation);
m_tilesprite.setOrigin(sf::Vector2f(0.f, 0.f));
m_tilesprite.setTexture(m_tiletexture);
while (!file.eof())
{
std::string str, value;
std::getline(file, str);
std::stringstream stream(str);
while (std::getline(stream, value, ' '))
{
if (value.length() > 0)
{
std::string xx = value.substr(0, value.find(','));
std::string yy = value.substr(value.find(',') + 1);
int x, y, i, j;
for (i = 0; i < xx.length(); i++)
{
if (!isdigit(xx[i]))
break;
}
for (j = 0; j < yy.length(); j++)
{
if (!isdigit(yy[j]))
break;
}
x = (i == xx.length()) ? atoi(xx.c_str()) : -1;
y = (j == yy.length()) ? atoi(yy.c_str()) : -1;
m_tempmap.push_back(sf::Vector2i(x, y));
}
}
m_map.push_back(m_tempmap);
m_tempmap.clear();
}
}
}
m_tempmap is a vector of sf::Vector2i, used to be pushed into m_map.
I'm running on 64-bit Windows 7 OS, compiling with Visual Studio Community 2013 and SFML version is the latest, 2.3.2.
Sorry for the long post. :-[
So i did my best to create a complete and minimal example, here's the code:
#include <SFML/Graphics.hpp>
int main()
{
sf::Vector2i screenDimensions(1280, 720);
sf::RenderWindow wnd(sf::VideoMode(screenDimensions.x, screenDimensions.y), L"Test");
sf::View cam(wnd.getDefaultView());
wnd.setView(cam);
const int level[8][16] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
wnd.setFramerateLimit(60);
// Create clock to measure frame time
sf::Clock frameClock;
wnd.setKeyRepeatEnabled(false);
sf::RectangleShape shape(sf::Vector2f(10.f, 10.f));
shape.setPosition(screenDimensions.x / 4.f, screenDimensions.y / 2.f);
sf::Vector2f movement(0.f, 0.f);
float speed = 200.f;
while (wnd.isOpen())
{
sf::Event event;
while (wnd.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
wnd.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::I)
wnd.setFramerateLimit(0);
else if (event.key.code == sf::Keyboard::O)
wnd.setFramerateLimit(60);
break;
}
}
sf::Time frameTime = frameClock.restart();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::F))
shape.setPosition(screenDimensions.x / 4.f, screenDimensions.y / 2.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) && !sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
movement.x -= speed;
shape.move(movement * frameTime.asSeconds());
movement = sf::Vector2f(0.f, 0.f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) && !sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
movement.x += speed;
shape.move(movement * frameTime.asSeconds());
movement = sf::Vector2f(0.f, 0.f);
}
if (shape.getPosition().x + shape.getSize().x > screenDimensions.x / 2)
{
cam.setCenter(shape.getPosition().x + (shape.getSize().x / 2), shape.getPosition().y);
wnd.setView(cam);
}
wnd.clear();
sf::View veiw = wnd.getView();
sf::FloatRect screenRect(veiw.getCenter() - (veiw.getSize() / 2.f),
veiw.getSize());
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 16; j++)
{
if (screenRect.intersects(sf::FloatRect(level[i][j] * 32, level[i][j] * 32, 32, 32)))
{
sf::RectangleShape rect(sf::Vector2f(32.f, 32.f));
rect.setPosition(j * 32, i * 32);
wnd.draw(rect);
}
}
}
wnd.draw(shape);
wnd.display();
}
return 0;
}
Sorry for taking so long to reply.
On a side note, i'm now compiling with Visual Studio 2015.