Vertex array tile sprites

Pages: 12
Ok so I found out that drawing my tiles to the editor was a really bad thing to do because it created too many sprites and textures and draw calls that it made the program lag, so I found out that vertex arrays are better but i'm having a really hard time getting it set up like it was, My vertex arrays dont draw to the grid.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>

using namespace std;

sf::Texture spriteSheetTexture;
sf::Sprite spriteSheet;
//if(!spriteSheetTexture.loadFromFile(""))
//{

//}

void LoadSpriteSheet()
{

}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "Tile Editor");
    sf::Event event;
    sf::Mouse mouse;
    sf::View scrollScreen;

    scrollScreen.reset(sf::FloatRect(0, 0, window.getSize().x, window.getSize().y));
    scrollScreen.setViewport(sf::FloatRect(0, 0, 1.0f, 1.0f));

    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                {
                    window.close();
                }break;
                case sf::Event::Resized:
                {
                    sf::FloatRect viewArea(0, 0, event.size.width, event.size.height);
                    window.setView(sf::View(viewArea));
                }
            }
        }
    window.clear(sf::Color::White);

    //Convert Pixels to new screen size
    sf::Vector2i pixel_pos = sf::Mouse::getPosition(window);
    sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);

    sf::VertexArray Square(sf::Quads, 4);
    sf::RenderStates states;

    Square[0].position = sf::Vector2f(0, 0);
    Square[1].position = sf::Vector2f(32, 0);
    Square[2].position = sf::Vector2f(32, 32);
    Square[3].position = sf::Vector2f(0, 32);

    Square[0].texCoords = sf::Vector2f(0, 0);
    Square[0].texCoords = sf::Vector2f(32, 0);
    Square[0].texCoords = sf::Vector2f(32, 32);
    Square[0].texCoords = sf::Vector2f(0, 32);


    sf::Texture blankGridTexture;
    sf::Sprite blankGridSprite;
    if(!blankGridTexture.loadFromFile("Sign.png"))
    {

    }
    blankGridSprite.setTexture(blankGridTexture);
    blankGridSprite.setOrigin(0, 0);

    //Load Grass
    sf::Texture tileTexture;
    sf::Sprite tileSprite;
    if(!tileTexture.loadFromFile("Outside_A5.png"))
    {

    }
    tileSprite.setTexture(tileTexture);
    tileSprite.setOrigin(16, 16);

    states.texture = &tileTexture;

    sf::Vector2f Grid(30, 30);

    Grid.x = 30;
    Grid.y = 30;

    if(sf::Event::KeyPressed)
    {
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            scrollScreen.move(-1, 0);
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            scrollScreen.move(1, 0);
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            scrollScreen.move(0, -1);
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        {
            scrollScreen.move(0, 1);
        }
    }

    //Draw Grid
    for(int i = 0; i < Grid.x; i++)
    {
        for(int j = 0; j < Grid.y; j++)
        {
            blankGridSprite.setPosition(j * 32, i * 32); // <- this is causing 1 grass tile and 1 sign tile to appear randomly
            window.draw(blankGridSprite);
        }
    }

    //Set tiles on grid - setMapTileSprite
    if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
    {
        //Get Position of the mouse then divide by 32 and round down to integer, then times it by 32 to set grid.
        states.transform.translate(sf::Vector2f(rint(coord_pos.x / 32) * 32, rint(coord_pos.y / 32) * 32));
    }

    window.setView(scrollScreen);
    window.draw(Square, states);
    window.display();
    }

    return 0;
}
Last edited on
Ok so I found out that drawing my tiles to the editor was a really bad thing to do because it created too many sprites and textures and draw calls that it made the program lag


Sprites are extremely lightweight. You should be able to have thousands and thousands of them without a problem.

Textures are heavyweight. But you should not have the same image loaded into multiple textures.


The idea for the separation is for this very reason: It allows you to have multiple sprites which share 1 texture. That way you do not need to load the same image multiple times for all your in-game objects which need it. You just load the texture once and hand that texture off to each sprite.

so I found out that vertex arrays are better


A vertex array of quads is not much better than a bunch of sprites. Not to mention it's considerably more work to use (as you are finding).


I recommend you do not abandon sprites so quickly, and instead go back to the code you had before and try to narrow down why it's lagging and fix it.
Ok so What I did is output the lists size, and with one click I created 35 sprites in that one spot, so thats the reason why, in 4 seconds of drawing i created over 5,000 sprites lol. Well I 'll see what solution I can come up with. Thank you.
Ok I need a hint. I know that what I need to do is check and see if the sprite underneath is the same texture and even if it isnt it needs to not draw hundreds at a time and it has something to do with the iterator too. but i'm stumped:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>

using namespace std;

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "Tile Editor");
    sf::Event event;
    sf::Mouse mouse;
    sf::View scrollScreen;

    list<sf::Sprite> setTile;
    list<sf::Sprite>::iterator iter;

    scrollScreen.reset(sf::FloatRect(0, 0, window.getSize().x, window.getSize().y));
    scrollScreen.setViewport(sf::FloatRect(0, 0, 1.0f, 1.0f));

    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                {
                    window.close();
                }break;
                case sf::Event::Resized:
                {
                    sf::FloatRect viewArea(0, 0, event.size.width, event.size.height);
                    window.setView(sf::View(viewArea));
                }
            }
        }
    window.clear(sf::Color::White);

    sf::Vector2i pixel_pos = sf::Mouse::getPosition(window);
    sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);


    sf::Texture blankGridTexture;
    sf::Sprite blankGridSprite;
    if(!blankGridTexture.loadFromFile("Resources/Tiles/Sign.png"))
    {

    }
    blankGridSprite.setTexture(blankGridTexture);
    blankGridSprite.setOrigin(16, 16);

    //Load Grass
    sf::Texture tileTexture;
    sf::Sprite tileSprite;
    if(!tileTexture.loadFromFile("Resources/Tiles/Grass.png"))
    {

    }
    tileSprite.setTexture(tileTexture);
    tileSprite.setOrigin(16, 16);

    sf::Vector2f Grid(30, 30);

    if(sf::Event::KeyPressed)
    {
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            scrollScreen.move(-1, 0);
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            scrollScreen.move(1, 0);
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
        {
            scrollScreen.move(0, -1);
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
        {
            scrollScreen.move(0, 1);
        }
    }

    //Draw Grid
    for(int i = 0; i < Grid.x; i++)
    {
        for(int j = 0; j < Grid.y; j++)
        {
            blankGridSprite.setPosition(j * 32, i * 32); // <- this is causing 1 grass tile and 1 sign tile to appear randomly
            window.draw(blankGridSprite);
        }
    }

    //Set tiles on grid - setMapTileSprite
    if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
    {
        //Get Position of the mouse then divide by 32 and round down to integer, then times it by 32 to set grid.
        tileSprite.setPosition(rint(coord_pos.x / 32) * 32, rint(coord_pos.y / 32) * 32);
        setTile.push_back(tileSprite);
    }

    iter = setTile.begin();

    while(iter != setTile.end())
    {
        window.draw(*iter);
        iter++;
    }

    cout << setTile.size() << endl;

    window.setView(scrollScreen);
    window.draw(tileSprite);
    window.display();
    }

    return 0;
}
Last edited on
this is causing 1 grass tile and 1 sign tile to appear randomly


Are you sure about that? Because it shouldn't be.



Anyway... just FYI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    //  This:
    iter = setTile.begin();

    while(iter != setTile.end())
    {
        window.draw(*iter);
        iter++;
    }


    // Is just a clunkier way of doing this  (C++11):
    for(auto& i : setTile)
    {
        window.draw(i);
    }


But that's not your problem.

Also... the if statement on line 64 is pointless... sf::Event::KeyPressed is a non-zero constant, so that if statement is always going to be considered to be true.

But that's not your problem either.



Your problem is....

Your logic is running as fast as your computer will allow. IE: you don't set the framerate, and you never sleep... so your program just runs and runs without a break.

That alone isn't a problem... but this means that your logic code is happening wicked fast. Probably several hundred times per second.

Now... look at line 95. That if statement will check the real time state of the left mouse button and will add a sprite if the mouse button is down.

So... say the user clicks the mouse... and it takes them 1/4th of a second for them to actually release the mouse button.

And let's say that you are running 300 logic updates per second.

300 / 4 = 75. So every time you click, you could be adding (and therefore drawing) 75 new sprites. More if you are holding down the mouse button longer.


EDIT: What you might want to do... is you might want to do this when the mouse button transitions from Released->Pressed ... rather than checking the real-time state. You could accomplish that by looking for the button down event... which is only sent once per click. /EDIT



So yeah... that's the problem.




But your sprite setup is also kind of weird. You could probably simplify this greatly. Usually tile systems like this are arranged so that each spot on the grid is exactly 1 'tile' or object... so you could just have an array of sprites... where their position in the array specifies where to draw them. You would never have 2 tiles/sprites in the same part of the grid.
Last edited on
" Usually tile systems like this are arranged so that each spot on the grid is exactly 1 'tile' or object... so you could just have an array of sprites... where their position in the array specifies where to draw them."

I'm trying to do something liek that now but how do i get the sprites positions in the grid? I dont want to do a static grid that's already pre planned out, I know how to do that. Do I set the sprite equal to a number?

I added a framerate limit too, it still draws too many tiles but once i figure out the new grid, i'll just replace the texture on the grid.

How would I get the tiles position on the grid when my mouse clicks on it? I mean I did something like this before:

cout << "Row: " << rint(mouse.getPosition(window).x / 32) << ",";
cout << "Column: " << rint(mouse.getPosition(window).y / 32) << endl;

And that displayed what row and column you were on but that really isnt setting anything equal to the elements in the array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>

using namespace std;

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "Tile Editor");
    sf::Event event;
    sf::Mouse mouse;
    sf::Clock clock;
    sf::Time time;
    sf::View scrollScreen;
    list<sf::Sprite> setTile;
    list<sf::Sprite>::iterator iter;
    int GridSizeX;
    int GridSizeY;
    window.setFramerateLimit(60);

    scrollScreen.reset(sf::FloatRect(0, 0, window.getSize().x, window.getSize().y));
    scrollScreen.setViewport(sf::FloatRect(0, 0, 1.0f, 1.0f));

    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                {
                    window.close();
                }break;
                case sf::Event::Resized:
                {
                    sf::FloatRect viewArea(0, 0, event.size.width, event.size.height);
                    window.setView(sf::View(viewArea));
                }
            }
        }
    window.clear(sf::Color::White);

    time = clock.getElapsedTime();

    sf::Vector2i pixel_pos = sf::Mouse::getPosition(window);
    sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);


    sf::Texture blankGridTexture;
    sf::Sprite blankGridSprite;
    if(!blankGridTexture.loadFromFile("Resources/Tiles/Grid.png"))
    {

    }
    blankGridSprite.setTexture(blankGridTexture);
    blankGridSprite.setOrigin(16, 16);

    //Load Grass
    sf::Texture tileTexture;
    sf::Sprite tileSprite;
    if(!tileTexture.loadFromFile("Resources/Tiles/Grass.png"))
    {

    }
    tileSprite.setTexture(tileTexture);
    tileSprite.setOrigin(16, 16);

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
    {
        scrollScreen.move(-11, 0);
    }
    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
    {
        scrollScreen.move(11, 0);
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
    {
        scrollScreen.move(0, -11);
    }
    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
    {
        scrollScreen.move(0, 11);
    }

    GridSizeX = 20;
    GridSizeY = 20;

    int tileGrid[GridSizeX][GridSizeY];
    //Draw Grid
    for(int i = 0; i < GridSizeX; i++)
    {
        for(int j = 0; j < GridSizeY; j++)
        {
            blankGridSprite.setPosition(j * 32, i * 32);
            window.draw(blankGridSprite);
        }
    }
/*
    if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
    {
        cout << "Clicked" << endl;
        //Get Position of the mouse then divide by 32 and round down to integer, then times it by 32 to set grid.
        tileSprite.setPosition(rint(coord_pos.x / 32) * 32, rint(coord_pos.y / 32) * 32);
        setTile.push_back(tileSprite);
    }
*/
    if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
    {
        if(coord_pos.x  )
        tileSprite.setPosition(rint(coord_pos.x / 32) * 32, rint(coord_pos.y / 32) * 32);
        //setTile.push_back(tileSprite);
    }

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::L))
    {
    }

    iter = setTile.begin();

    while(iter != setTile.end())
    {
        window.draw(*iter);
        iter++;

    }

    cout << setTile.size() << endl;
    cout << time.asSeconds() << endl;

    window.setView(scrollScreen);
    window.draw(tileSprite);
    clock.restart();
    window.display();
    }

    return 0;
}
Last edited on
I'm trying to do something liek that now but how do i get the sprites positions in the grid? I dont want to do a static grid that's already pre planned out, I know how to do that. Do I set the sprite equal to a number?


Have an array of sprites. When you draw... just step through the array and draw each sprite.

The position of the sprite should be determined by their position in the array. IE:

[0] @ y=0,x=0
[1] @ y=0,x=1
[width] @ y=1,x=0
[width+1] @ y=1,x=1
etc

Or... if you want to do a 2D array instead (that might be more intuitive, but I freaking hate working with 2D arrays personally):

[0][0] @ y=0, x=0
[0][1] @ y=0, x=1
[1][0] @ y=1, x=0
[1][0] @ y=1, x=1
etc



And that displayed what row and column you were on but that really isnt setting anything equal to the elements in the array.


That's because your current array is weird. When you redo your array, that position will be the array index of the sprite you want to change.
"Have an array of sprites. "

Like this?

1
2
3
4
5
6
7
8
9
10
11
12
13
    GridSizeX = 20;
    GridSizeY = 20;

    sf::Sprite tileGrid[GridSizeX][GridSizeY];
    //Draw Grid
    for(int i = 0; i < GridSizeX; i++)
    {
        for(int j = 0; j < GridSizeY; j++)
        {
            blankGridSprite.setPosition(j * 32, i * 32);
            window.draw(blankGridSprite);
        }
    }
Basically, yeah. Only instead of drawing the blankGridSprite, you'd draw the sprite from tileGrid.

EDIT:

And usually 2D arrays are row-major -- ie: [y][x] instead of [x][y]. But it doesn't really matter. As long as you don't get mixed up.
Last edited on
Oh ok I changed it, So if i wanted to set a texture i would do:

1
2
3
4
5
6
if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
    {
        //tileSprite.setPosition(rint(coord_pos.x / 32) * 32, rint(coord_pos.y / 32) * 32);
        //setTile.push_back(tileSprite);
        tileGrid[4][2].setTexture(tileTexture);
    }
Yuuup
ok, well for some reason that didnt work, I dont need the list and iterator anymore do i? I have that commented out.

EDIT: Wait, I need to draw tileGrid... But when i do this my program crashes

window.draw(tileGrid[GridSizeX][GridSizeY]);

Since this is a sprite array, numbers wouldnt go in the indexes would they?
Last edited on
Since this is a sprite array, numbers wouldnt go in the indexes would they?


Yes... they would. It doesn't matter what you have an array of. Arrays always take integers for indexes.

The problem is you are trying to draw a non-existent sprite because you are stepping out of bounds of your array.

1
2
3
4
5
6
7
8
9
10
11
12
13
int foo[5][5];  // <- have a 5x5 array

foo[4][4] = 0; // <- so [4][4] is the MAXIMUM index

foo[5][5] = 0;  // <- OUT OF BOUNDS -- EXPLODE

// ....

Sprite tileGrid[GridSizeX][GridSizeY];  // <- XxY array

draw( tileGrid[GridSizeX-1][GridSizeY-1] ); // <- MAXIMUM index

draw( tileGrid[GridSizeX][GridSizeY] ); // <- OUT OF BOUNDS -- EXPLODE 
Last edited on
How is it outof bounds, i'm doing it here:

1
2
3
4
5
6
7
8
9
for(int i = 0; i < GridSizeY; i++)
    {
        for(int j = 0; j < GridSizeX; j++)
        {
            tileGrid[GridSizeY][GridSizeX];
            window.draw(tileGrid[GridSizeY][GridSizeX]);
            //window.draw(blankGridSprite);
        }
    }


Which is exactly what I would do with a normal sprite.
How is it outof bounds, i'm doing it here:
...
Which is exactly what I would do with a normal sprite.


Not exactly.


Forget the 2D array for now... look at this in terms of an 1D array:

1
2
3
4
5
6
7
int foo[10];  // <- a 10 element array

// this is what you are doing:
for(int i = 0; i < 10; ++i)
{
    foo[10] = 5;  // <- this is out of bounds
}
whaaa? I dont even understand that? I don't get how its out of bounds. Maybe I'm just not thinking very well idk, I NEVER use arrays so it's a bit strange. I always use vectors.

So I would guess the only option is to put it in the for loop itself like this right:

1
2
3
4
5
6
7
int foo[10];  // <- a 10 element array

// this is what you are doing:
for(int i = 0; i <  foo[10]; ++i)
{

}
Last edited on
int foo[10];
foo has space for 10 elements, and the first index is at 0:

index      0      1      2      3      4      5      6      7      8      9      10
element [  1  ][  2  ][  3  ][  4  ][  5  ][  6  ][  7  ][  8  ][  9  ][  10 ][  ?  ]


Index 10 refers to the 11th element of an array that only has 10 elements. Thus, it is out of bounds.

vectors use the same indexing scheme.
So I need to do:

1
2
3
4
5
6
7
int foo[10];  // <- a 10 element array

// this is what you are doing:
for(int i = 0; i <  foo[10 + 1]; ++i)
{

}


?
No... you are getting colder.

Maybe you need clarification on how for loops work. I give some detail here:

http://www.cplusplus.com/forum/beginner/105846/#msg571996

So now let's take a look at your for loop:

 
for(int i = 0; i <  foo[10 + 1]; ++i)


What is your loop condition here? The condition is i < foo[10 + 1]... but what is foo[10+1]? It's an out of bounds (non-existent) element of your foo array!



EDIT:

You said you know how to use a vector. Arrays are basically the same idea as vectors... only they can't be resized.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int foo[3];  // an array with 3 elements.  This creates 3 different 'int's:
//  foo[0] is an int
//  foo[1] is an int
//  foo[2] is an int

// foo[x] where x is anything other than 0,1,2 is BAD because it is trying to access an int that
// doesn't exist.  Only [0],[1],[2] exist.

// So all of this is OK:
foo[0] = 5;
foo[1] = 10;
foo[2] = 3;

// But this is bad:
foo[3] = 7; // OUT OF BOUNDS


// Arrays are commonly used with for loops to do an action on each element in the array:
//  This loop will loop 3 times.  Each time, i will be incremented by 1.
//  Therefore the body will run 3 times:
//  - once when i==0
//  - once when i==1
//  - once when i==2
//  Then the loop will stop because i==3
for(int i = 0; i < 3; ++i)
{
    cout << foo[i]; // this will print foo[i]... and since i is always 0,1 or 2... this means it
       // will print foo[0], foo[1], or foo[2]
}
Last edited on
Well if an array cant be resized then i need to just use a vector because i want the user to resize their map if need be. so i'll come up with some code and see if that works and i'll be back.
Pages: 12