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

Author Topic: Tileset/Sprite - Design question  (Read 7911 times)

0 Members and 1 Guest are viewing this topic.

Ben

  • Newbie
  • *
  • Posts: 4
    • View Profile
Tileset/Sprite - Design question
« on: March 22, 2008, 11:19:33 pm »
Hello,

I'm pretty new to SFML. Previously I've been working with SDL and Allegro and they handle bliting differently than SFML with it's Sprites.

Suppose I have one tileset in form of one image. Now I want to draw a game map onto the screen (SimCity style). Now I don't know how this is supposed to be done with the Sprite class. Here are the possible ways the come to my mind:

1. Create a new Sprite for each blit. Meaning for each drawing operation in a frame create a Sprite use SetPosition, SetSubRect & Draw and then destroy it again.

2. Create each frame a new sprite then use SetPosition, SetSubRect & Draw for each tile and destroy the sprite once the job is done.

3. Same as 2. but with a sprite that has the same lifetime as the image.

4. For each tile in the set create one Sprite and then each frame use SetPosition & Draw to do the job.

5. For each map square on the screen create a Sprite and then use SetSubRect & Draw each frame.

Which of these 4 ways was intended to be used?

Should I think of a Sprite as a leight-weight throw away object (such as IntRect for example) or as a more resource intensive object?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Tileset/Sprite - Design question
« Reply #1 on: March 23, 2008, 06:04:31 am »
A sprite is a very light object, you shouldn't be afraid of using a lot of them. The heavy thing is the image, which you should have only one instance in memory for your tileset.

To me, the best solution would be to store one sprite per tile and just draw them each frame.
Laurent Gomila - SFML developer

Ben

  • Newbie
  • *
  • Posts: 4
    • View Profile
Tileset/Sprite - Design question
« Reply #2 on: March 23, 2008, 10:30:47 am »
Thanks for the answer, and great lib btw :)

Avency

  • Full Member
  • ***
  • Posts: 113
    • View Profile
Tileset/Sprite - Design question
« Reply #3 on: March 23, 2008, 03:56:26 pm »
It depends on the type of game you are trying to create.

Quote

2. Create each frame a new sprite then use SetPosition, SetSubRect & Draw for each tile and destroy the sprite once the job is done.

I've tried this some time ago. It worked but was a bit slow (where slow can be seen relative). But for a game like SimCity it should be sufficient.
Quote

To me, the best solution would be to store one sprite per tile and just draw them each frame.

This can be quite memory consuming for big maps. But it depends on your target hardware.

My current attempt is to store an integer for each tile and then to calculate its texture coordinates in the rendering loop and blit it to the screen using OpenGL directly.
This methods kicks ass (ca. 4 times faster than the method above and can handle really big maps). :D

Another method would be to create a tileset class storing a sprite for each tile and precalculating their subrects.
In the render-loop, you would only have to decide which sprite should be drawn at which position. Or maybe creating a tile class that inherits sf::Sprite.

But as said above, it heavily depends on what you are trying to achieve.

Dummie

  • Newbie
  • *
  • Posts: 25
    • View Profile
Tileset/Sprite - Design question
« Reply #4 on: March 23, 2008, 06:47:03 pm »
Hey guys,

Here you can see my try. This is very slow on my PC.

Code: [Select]

#include <iostream>
#include <ctime>
#include <SFML/Graphics.hpp>

void setTile(sf::Sprite& sprite, const int x = 0, const int y = 0);

struct MapData {
sf::Sprite sprite;
bool moveable;
};

MapData map[1024/32][768/32];

int main() {
sf::Image playerImage;
playerImage.LoadFromFile("data/gfx/player.bmp");
playerImage.CreateMaskFromColor(sf::Color(255,0,255));
sf::Sprite playerSprite(playerImage);

sf::RenderWindow app(sf::VideoMode(1024, 768, 32), "Tilemap");
sf::Image tilesImage;
tilesImage.LoadFromFile("data/gfx/tiles.bmp");
tilesImage.CreateMaskFromColor(sf::Color(255,0,255));

for (int i = 0; i < 1024/32; ++i) {
for (int j = 0; j < 768/32; ++j) {
map[i][j].sprite = sf::Sprite(tilesImage);
int x = sf::Randomizer::Random(0, 3);
int y = 0;
setTile(map[i][j].sprite, x, y);
map[i][j].sprite.SetLeft(float(i*32));
map[i][j].sprite.SetTop(float(j*32));
if (x == 0) {
map[i][j].moveable = true;
} else {
map[i][j].moveable = false;
}
}
}

/*
!!! Just a space to start !!!
*/
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
int x = 0;
int y = 0;
setTile(map[i][j].sprite, x, y);
map[i][j].moveable = true;
}
}

bool running = true;
bool renderTiles = true;
while (running) {
sf::Event ev;
while (app.GetEvent(ev)) {
if (ev.Type == sf::Event::Closed) {
running = false;
}
}

const float elapsedTime = app.GetFrameTime();

if (app.GetInput().IsKeyDown(sf::Key::Left)) playerSprite.SetLeft(playerSprite.GetLeft() - 100 * elapsedTime);
if (app.GetInput().IsKeyDown(sf::Key::Right)) playerSprite.SetLeft(playerSprite.GetLeft() + 100 * elapsedTime);
if (app.GetInput().IsKeyDown(sf::Key::Up)) playerSprite.SetTop(playerSprite.GetTop() - 100 * elapsedTime);
if (app.GetInput().IsKeyDown(sf::Key::Down)) playerSprite.SetTop(playerSprite.GetTop() + 100 * elapsedTime);
              // Debug
if (app.GetInput().IsKeyDown(sf::Key::F1)) renderTiles = true;
if (app.GetInput().IsKeyDown(sf::Key::F2)) renderTiles = false;

if (renderTiles) {
for (int i = 0; i < 1024/32; ++i) {
for (int j = 0; j < 768/32; ++j) {
app.Draw(map[i][j].sprite);
}
}
}

app.Draw(playerSprite);
app.Display();
}
return 0;
}

void setTile(sf::Sprite& sprite, const int x, const int y) {
sf::Rect<int> rect;
rect.Left = 32 * x;
rect.Right = 32 + 32 * x;
rect.Bottom = 32 + 32 * y;
rect.Top = 32 * y;
sprite.SetSubRect(rect);
}


Maybe I will try another method later :)

zarka

  • Jr. Member
  • **
  • Posts: 81
    • View Profile
Tileset/Sprite - Design question
« Reply #5 on: March 23, 2008, 10:29:32 pm »
the best solution would probably be to learn some OpenGL (nothing fancy is needed) and make a new class that inherits from sf::Drawable.

very simplistic untested written on the fly example
Code: [Select]


class CGameMap : public sf::Drawable
{
public:
protected:
    void Render(const sf::RenderWindow &wnd) const;
private:
    sf::Image mySpriteSheet;
    std::vector<sf::IntRect> myTiles;
};

void CGameMap::Render(const sf::RenderWindow &wnd) const
{
    //since all tiles recide in one image we can bind it once to skip unnecessary texture switches
    mySpriteSheet.bind();

    //Loop through all tiles get their position and what tile to use.
    for(iterate through myTiles)
    {
       sf::FloatRect texCoords = mySpriteSheet.GetTexCoords((*iter));
       float screenPosX = the position of the tile in x;
       float screenPosY = the position of the tile in y;
       glTranslatef(screenPosX, screenPosY, 0.f);

       glBegin();
       glTexCoord2f(texCoords .Left,  texCoords .Top);    glVertex2f(0,     0);
            glTexCoord2f(texCoords .Left,  texCoords .Bottom); glVertex2f(0,     Height);
            glTexCoord2f(texCoords .Right, texCoords .Bottom); glVertex2f(Width, Height);
            glTexCoord2f(texCoords .Right, texCoords .Top);    glVertex2f(Width, 0);
       glEnd();
    }
}



this could also be put in  adisplay list or something to avoid imediate mode drawing :)
//Zzzarka

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Tileset/Sprite - Design question
« Reply #6 on: March 24, 2008, 02:37:54 am »
Yep, using a display list and custom OpenGL calls would definitively be the best solution.

However, adding app.OptimizeforNonOpenGL(true) should be enough for you ;)
Laurent Gomila - SFML developer

Dummie

  • Newbie
  • *
  • Posts: 25
    • View Profile
Tileset/Sprite - Design question
« Reply #7 on: March 24, 2008, 04:13:12 pm »
Hey Laurent,

Works very great for me! :)

But could you be so nice and answer here?
http://www.sfml-dev.org/forum/viewtopic.php?t=277

I'm a little bit stucking... :roll:

Thanks :)