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

Author Topic: Help! Regarding view and world drawing  (Read 9284 times)

0 Members and 1 Guest are viewing this topic.

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Help! Regarding view and world drawing
« on: March 08, 2016, 10:52:00 am »
Hi,

I'm not experienced at all, but i did write a couple of games. I just started with a new one now, but since i want to do this right from the get-go i wanted to double check if the way i go about drawing the world map is in fact the right way to go (or rather - my best option)

Assuming an RPG like game with a tilebased map (standard stuff i suppose).

1. i'd load my map into a 2-dimensional vector (for x, y coords)
2. Each map location has a vector of tiles (so i can layer tiles: i.e: water -> bridge -> signpost
3. At least 25 frames per second i call the draw function
4. The draw function loops through the map vector and compares x,y coordinates with view x,y coords
5. Everything that's within the view is redrawn, keeping tile order in mind per map position

Does this make sense? Or is this highly ineffective compared to an alternative (like drawing the entire tile map only once and then update those tiles requiring change).

kind regards,
Bitano
« Last Edit: March 08, 2016, 01:46:51 pm by bitano »

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: regarding view and world drawing
« Reply #1 on: March 08, 2016, 11:33:32 am »
Giving it some more thought, i'd say that (next to having the map vector) i'd have to:

1. Draw the entire map to a huge canvas (maybe sf::Texture?)
2. in the draw function clip the part of the texture map that's shown by the view

If this is the way to go, wouldn't this generate a really large texture if i'd have a big world (say 500x500 tiles)?

Assuming tiles are 100x100 pixels, the texture data size in bytes would be something like:
4 (rgba pixel) x 100 x 100 x 500 x 500 bytes = 9.3 gigs (?!?!)

Clearly, i'm confused!

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: regarding view and world drawing
« Reply #2 on: March 08, 2016, 05:08:08 pm »
Draw the entire map to a huge canvas (maybe sf::Texture?)
I think you may be looking for sf::RenderTexture.

It should be fine to draw all the tiles directly to the window as long as the ones that are not in display are culled. Combining the tiles and drawing them as a vertex array would also significantly reduce the number of draw calls from, say, drawing each tile as a separate sf::Sprite.

The only thing that you need to store completely is the definition of the map's tiles. The actual drawing of them can be done simply and quickly each frame - again, as long as you only draw the ones that are within the window.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: regarding view and world drawing
« Reply #3 on: March 08, 2016, 07:57:17 pm »
I think you may be looking for sf::RenderTexture.
Thanks! That's exactly it!

It should be fine to draw all the tiles directly to the window as long as the ones that are not in display are culled. Combining the tiles and drawing them as a vertex array would also significantly reduce the number of draw calls from, say, drawing each tile as a separate sf::Sprite.

I don't fully see the advantage yet of a renderTexture as opposed to drawing directly to the renderWindow (for the world tiles).
I can imagine setting up a renderTexture for the GUI and keep track of whether it requires redrawing (as the gui will change less often). But the game world is pretty much in constant change due to hero and monster movement and tile animations.

I'm quite used to culling hidden tiles, so that's alright. Thanks for the tip though.
I'm not familiar with the vertex array. Sounds interesting! I will look into this as well. Thanks!
« Last Edit: March 08, 2016, 08:00:11 pm by bitano »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Help! Regarding view and world drawing
« Reply #4 on: March 08, 2016, 09:20:58 pm »
I can imagine setting up a renderTexture for the GUI and keep track of whether it requires redrawing (as the gui will change less often). But the game world is pretty much in constant change due to hero and monster movement and tile animations.
I don't think GUI is the reason to use a render texture. Apart from the fact that it would likely change often, with it giving live feedback, it should be absolutely fine to just draw the entire UI directly to the window each frame.

I don't fully see the advantage yet of a renderTexture as opposed to drawing directly to the renderWindow (for the world tiles).
There are a few of reasons why you might do this. One is to help alleviate the slight "tile-edging" error that can occur when tiles are transformed to where this occurs. Another is to apply cropping or effects, with effects probably being applied via a shader.

I'm not familiar with the vertex array. Sounds interesting! I will look into this as well. Thanks!
If your map is a regular grid and is stored as an array (or vector etc.) of tile numbers, you may be interested in sw::TileMap, which should do the majority of the ground-work. It internally uses a render texture for a few reasons - the two above are a couple of them.
However, if you want to tackle this manually, this is the official tutorial for vertex arrays.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #5 on: March 08, 2016, 10:53:02 pm »
OK cool. Thanks a lot for the input.

I get the offscreen drawing to prevent unwanted visual effects. No idea why i didn't think of that myself as i used to do that with some previous projects (not SFML related...the word blitting comes to mind, but can't recall from where :-D).
So yea...

1. Draw visible tiles to offscreen renderTexture
2. Draw PCs, NPCs and objects to offscreen renderTexture
3. Apply effects / mask / lighting / what not  to offscreen renderTexture
4. Draw the GUI to offscreen renderTexture
5. draw the renderTexture to the renderWindow
6. BACON

I think i understand how the vertex arrays work with a tileset straightfoward matrix of tile indices, but i'm struggling still with how it would fit into my current tilemap design. Same goes for sw::TileMap.

These seem to work only for single layer tilemaps. I'd like to work with a multi-layer tilemap though.
What i mean is, every wold tile is in fact an array of tiles so i can stack multiple tiles on the same spot.

For example: World tile (5,7) would have a tile vector which could contain the following tiles: water, bridge, sign.
So on that world spot you can see the animated water. On top of it a small, damaged bridge and on the bridge a small sign.

Just sharing my thoughts here for some feedback :-)

doingit

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #6 on: March 09, 2016, 03:26:43 am »
What about an array(vector) of sf::RenderTextures? Each render texture would represent a different layer. I'm assuming you want modification on different layers without having to redraw everything every single time to a single layer.
In this case, you would get n amount of layers, where n is equal to the number of render textures.

And then you would apply them in orderly fashion (order matters).
First to be drawn to the window would be the lowest level, last to be drawn would be the highest level.

Their is a possibility that I am not understanding the question correctly though =D.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Help! Regarding view and world drawing
« Reply #7 on: March 09, 2016, 04:09:25 am »
These seem to work only for single layer tilemaps. I'd like to work with a multi-layer tilemap though.
What i mean is, every wold tile is in fact an array of tiles so i can stack multiple tiles on the same spot.
sw::TileMap is designed to be used for static, single layered tile-maps. It could still be used, however, using a few of them drawn directly on top of each other for the layers and choosing the correct tile depending on the frame of animation but this becomes so manual that it's likely to be better to build a more bespoke tile map.
Vertex array would still be your port of call, though.
For the layers, you can either use a number of vertex arrays, where the higher ones only include the tiles that have things on that layer, or a single vertex array and just resize it (or don't send all vertices to the draw method) to include all tiles. All primitives are drawn in order so the ones at the end of the array would be drawn on top of the ones at the beginning.

Just to be clear, sf::VertexArrays aren't technically arrays; internally, they use std::vectors therefore they can be dynamically resized.

1. Draw visible tiles to offscreen renderTexture
2. Draw PCs, NPCs and objects to offscreen renderTexture
3. Apply effects / mask / lighting / what not  to offscreen renderTexture
4. Draw the GUI to offscreen renderTexture
5. draw the renderTexture to the renderWindow
If you are drawing the render texture directly to the window, there is no point in using the render texture because all of the drawing would be the same as the window and then it's just copied onto the window, so it would be the same as doing points 1-4 directly to the window.
Sorry; no bacon!
The render texture can come in useful if you need to modify the entire contents together - as one - before (or during using a shader/blendmode) drawing it to the window, such as scaling, moving, rotating.

What about an array(vector) of sf::RenderTextures? Each render texture would represent a different layer.
This could be useful if each layer needs to be processed individually but that's unlikely and probably rare.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #8 on: March 09, 2016, 09:27:30 am »
Right. So first: Am i understanding correctly that i'm creating / populating the sf::VertexArray for every frame drawn (so in the draw() function)?

Secondly, seeing as vertexArrays work with positions of textures in a large tilesheet, I'm considering generating the tilesheet on a renderTexture when loading the level. Reason for this is that i store my tiles separately in their own files. During gameplay however, tiles can be affected causing them to change. A full grassplot can turn into a half grassplot or disappear alltogether. If i'd use a rendered tilesheet, i would need to check whether the changed tile exists in the tilesheet and if not, add it (at the cost of performance).

The contents of a tile file look like this:



When a grass tile is affected by certain effects, it then can pick the appropriate subtile looking at surrounding tiles.

A lot of assumptions here! Feel free to correct :D

...I was really looking forward to the bacon :(
« Last Edit: March 09, 2016, 09:34:48 am by bitano »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Help! Regarding view and world drawing
« Reply #9 on: March 09, 2016, 08:38:43 pm »
Am i understanding correctly that i'm creating / populating the sf::VertexArray for every frame drawn (so in the draw() function)?
The draw() method should (usually) be used to draw the vertices to the target, not prepare the vertices. You could simply have an update() function that updates the vertices, which you would update each frame before drawing.
Possibly something like this:
// windowLoop:
{
    handleEvents();

    updateGameLogicEtc();

    updateGraphicsEtc(); // includes tilemap.update()

    window.clear();
    window.draw(graphics); // include window.draw(tilemap);
    window.display();
}

The update function might look something like this highlighted code in the update method of sw::TileMap

Secondly, seeing as vertexArrays work with positions of textures in a large tilesheet, I'm considering generating the tilesheet on a renderTexture when loading the level. Reason for this is that i store my tiles separately in their own files. During gameplay however, tiles can be affected causing them to change. A full grassplot can turn into a half grassplot or disappear alltogether. If i'd use a rendered tilesheet, i would need to check whether the changed tile exists in the tilesheet and if not, add it (at the cost of performance).
So, you want to create a texture programmatically and then use that for your tile map? Although render texture should work for that, I think a render texture might be overkill as it's not getting rendered to often.

An option is to use a temporary sf::Image to create the image texture that you want (you can copy parts of images around) and then transfer it to the sf::Texture that you use for the tile map.
Remember you can load file as images and only transfer to a texture when you need them. (sf::Texture internally uses an sf::Image to load the file and then transfers it)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #10 on: March 09, 2016, 10:01:20 pm »
Thanks a lot for your patience and detailed replies, Hapax. I really appreciate you taking the time to help me out. I'm learning a lot in just a couple of days and I fully agree with the "Hero member" title :-D

The draw() method should (usually) be used to draw the vertices to the target, not prepare the vertices. You could simply have an update() function that updates the vertices, which you would update each frame before drawing.

Possibly something like this:
// windowLoop:
{
    handleEvents();

    updateGameLogicEtc();

    updateGraphicsEtc(); // includes tilemap.update()

    window.clear();
    window.draw(graphics); // include window.draw(tilemap);
    window.display();
}
The update function might look something like this highlighted code in the update method of sw::TileMap
Check. The highlighted code is very useful. Also, yes - It would make more sense to have a separate update function, keeping the draw() function more straightforward.

So, you want to create a texture programmatically and then use that for your tile map? Although render texture should work for that, I think a render texture might be overkill as it's not getting rendered to often.

An option is to use a temporary sf::Image to create the image texture that you want (you can copy parts of images around) and then transfer it to the sf::Texture that you use for the tile map.
Remember you can load file as images and only transfer to a texture when you need them. (sf::Texture internally uses an sf::Image to load the file and then transfers it)
Alright. So what if:

1. i'd use a vector <sf::Image> array (TileIndex), with each image representing a tile collection like the grass example i posted earlier -> [grasstiles image, dirttiles image, forestroad tiles, image, etc...]
2. Then use this vector <sf::Image> array as the source from which i copy the relevant tiles to a sf::Texture Tilesheet.
3. The Tilesheet is then used by the VertexArray to draw the sf::RenderTexture Tilemap (the visible part of the world).
...(4. bacon...?)
« Last Edit: March 09, 2016, 10:07:16 pm by bitano »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Help! Regarding view and world drawing
« Reply #11 on: March 10, 2016, 02:01:00 am »
Thanks a lot for your patience and detailed replies, Hapax. I really appreciate you taking the time to help me out.
You're welcome. I'm glad it's helping.

It would make more sense to have a separate update function, keeping the draw() function more straightforward.
I probably shouldn't point this out but it is, in fact, possible to update automatically in the draw method but it can get really messy (the draw method is const so it can't/shouldn't alter anything).
I did do that for sw::Sprite3D so that it would work in the same way as a normal sf::Sprite - without needing an extra update method. Feel free to have a look through the code ;D

1. i'd use a vector <sf::Image> array (TileIndex), with each image representing a tile collection like the grass example i posted earlier -> [grasstiles image, dirttiles image, forestroad tiles, image, etc...]
2. Then use this vector <sf::Image> array as the source from which i copy the relevant tiles to a sf::Texture Tilesheet.
3. The Tilesheet is then used by the VertexArray to draw the sf::RenderTexture Tilemap (the visible part of the world).
...(4. bacon...?)
1) vector of sf::Image for each tileset (as you said)
2) create a temporary sf::Image and use the vector to provide the pieces you want to copy, then update the sf::Texture from that image.
3) use the sf::Texture to draw the vertex array (no render texture needed?)
4) Oh, the poor pigs. Won't somebody please think of the pigs! :'(

This way, you'd have a single sf::Texture that you'd use for the tile map. You can reconstruct it via a temporary sf::Image and then just update the texture from that and the tile map wouldn't even notice!
Note that the image should not be larger than the texture. Most reliable design would be to create the (temporary) sf::Image to match the texture's size, copy the tiles onto that, then update the texture.

I'm not sure why you have a render texture although you can either use an sf::Image or an sf::RenderTexture in the place of the "temporary sf::Image" described above.
Creating a temporary render texture is probably slower than a temporary sf::Image but updating the texture from an image is slower than just using the render texture's internal texture as the texture for the vertex array. Your call  ;)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #12 on: March 10, 2016, 09:11:16 am »
I probably shouldn't point this out but it is, in fact, possible to update automatically in the draw method but it can get really messy (the draw method is const so it can't/shouldn't alter anything).
I did do that for sw::Sprite3D so that it would work in the same way as a normal sf::Sprite - without needing an extra update method. Feel free to have a look through the code ;D
Will do! I wanted to put it all in one draw() function, but i can appreciate keeping the logic separated in favor of code tidyness. This game is a relatively ambitious project (for me), so that's why i'm aiming for the best / most efficient approach.

1) vector of sf::Image for each tileset (as you said)
2) create a temporary sf::Image and use the vector to provide the pieces you want to copy, then update the sf::Texture from that image.
3) use the sf::Texture to draw the vertex array (no render texture needed?)
Can't get any clearer than this :-). Awesome!

I'm not sure why you have a render texture although you can either use an sf::Image or an sf::RenderTexture in the place of the "temporary sf::Image" described above.
The render Texture in the third step was more meant as drawing to the render object. It could/should have been RenderWindow, but i wrote renderTexture because it still feels strange to draw tile world directly to the window, instead of drawing it in the background and then display whole thing once.
It had nothing to do though with the process of selecting tiles and updating using the vertex :-)

This game is going to be amazing! Can't wait to put it all together! :D

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Help! Regarding view and world drawing
« Reply #13 on: March 10, 2016, 07:16:29 pm »
Looking forward to the result. Don't forget that the SFML forum has a dedicated Projects forum. ;)

it still feels strange to draw tile world directly to the window, instead of drawing it in the background and then display whole thing once.
When drawing to the window, it actually draws to a buffer - not the actual window. It then swaps this buffer with the window's content in one go when you call window.display() (which is why this is required).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

bitano

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Re: Help! Regarding view and world drawing
« Reply #14 on: March 10, 2016, 09:15:58 pm »
Looking forward to the result. Don't forget that the SFML forum has a dedicated Projects forum. ;)
Yeah i noticed. Some cool stuff there! At the moment it felt a bit premature as i don't have anything visual to show (obviously lol). But as soon as it's starting to take shape i will deff share my project!

When drawing to the window, it actually draws to a buffer - not the actual window. It then swaps this buffer with the window's content in one go when you call window.display() (which is why this is required).
Ah right...That makes sense!