Tiles Drawn from Different Backgrounds

G

Greg5000

Guest
Okay so I've been developing my project for a while now and I basically have a very large map (10000 x 10000 pixels) with two views, one main view focusing on the player and a mini map drawn as a surface on the GUI layer.

My entire background is tiled and what I now want to do is be able to show the tiles differently in each view, with colourful rich tiles in the main view and basic versions of these in the mini map.

What I have tried so far has been storing all tile ids in a 1D array, then I use the view_current along with the tile_set_background to try and set the tileset which the tiles are drawn from as different in each view, however this hasn't worked. So I tried adding all of the tiles twice with the map tiles on a separate layer, I then use the tile_layer_show and tile_layer_hide functions to show the map tiles in the mini map and the main tiles in the main view; this works but is causing a dramatic loss in frame rate.

Can anyone think of a more efficient way of achieving this? I'm basically just trying to get tiles to draw from different backgrounds in different views.
 

jo-thijs

Member
Hi and welcome to the GMC!

When you say you're drawing the mini map as a surface on the GUI layer, what exactly do you mean with that?
Are you using an extra view for te mini map or are you using the GUI event?
I would disrecommend using views.
It might be a good idea to have 1 object draw the mini map in either its draw end or draw GUI event.
You can use tile_layer_find in a nested for loop to find what tiles there are and decide what should be drawn on the mini map.
 
G

Greg5000

Guest
Hi, thanks for the reply, the mini map is in View 1 which is drawn to a surface using view_surface_id. This surface is then drawn in the GUI event to the top right of the display using a control object. Meanwhile view 0 (the main view) isn't drawn on a surface. The main problem I am looking to solve is whether I can draw the same tiles from different tilesets in each view, so the tiles I have added to the room on Layer 10000 will draw from one tileset in view 0 and from a different tileset in view 1.. Like I said I experimented with adding two sets of tiles on different layers, one for the main view, one for the mini map and then showing/hiding these in the different views but this dramatically reduced my FPS.
 

jo-thijs

Member
When you previously tried using tile_set_background,
in which event did you do that?
In the draw begin event?
That's the one it should have been in.
 
G

Greg5000

Guest
I added all code to the create event, so in the create event a 1D array is created to store all tile ids and then a nested for loop sets the background for the tiles differently in each view, it looks something like this:

var tiles = tile_get_ids_at_depth(100000);

if view_current = 0
{
for (var i = 0; i < array_length_1d(tiles); i++;)
{
tile_set_background(tiles, tileset_0);
}
}

if view_current = 1
{
for (var i = 0; i < array_length_1d(tiles); i++;)
{
tile_set_background(tiles, tileset_1);
}
}

The doesn't have the desired effect and the tiles just draw from the same background in both views... if I put this code in the Draw Begin Event won't it be called every step and cripple my game by looping through every tile repeatedly? I would test this out now but am not at my computer..
 

jo-thijs

Member
This should be in the create event:
Code:
var tiles = tile_get_ids_at_depth(100000);
This in the draw begin event:
Code:
if view_current = 0
{
for (var i = 0; i < array_length_1d(tiles); i++;)
{
tile_set_background(tiles, tileset_0);
}
}

if view_current = 1
{
for (var i = 0; i < array_length_1d(tiles); i++;)
{
tile_set_background(tiles, tileset_1);
}
}
EDIT:
Sorry, rushed this reply.

Yes, it's going to be called every step.
Even worse, it's going to be called every step for every view.
So, it's going to be called twice every step.
I do expect this to be very bad for the performance, but I'm not sure.

You can also try scanning over every cell inside the view and check what tile's there,
instead of changing the background for every tile.

You could create a 2D array in the create event that contains the id of every tile per cell.
Then you can loop through the relevant tiles.
This could save a lot of performance in big rooms, but it can consume quite some memory.
 
G

Greg5000

Guest
Will it not crash / slow down my game adding a for loop which is called at every step in the Draw Begin event? I previously had the second bit of code in the step event and it crippled the game..
 

jo-thijs

Member
I edited my reply to answer this question.
I do think it will, but you can try only changing the backgrounds of the tiles in view.
 
G

Greg5000

Guest
Thanks for the reply, much appreciated.. I've never worked with 2D arrays before, how would I go about scanning only the cells in view?
 

jo-thijs

Member
You would scan the entire room in the create event:
Code:
for(var i = (room_width - 1) div gridW; i >= 0; i--)
    for(var j = (room_height - 1) div gridH; j >= 0; j--)
        tile_array[i, j] = -1;

var tiles = tile_get_ids_at_depth(100000);
for(var i = array_length_1d(tiles) - 1; i >= 0; i--)
    tile_array[tile_get_x(tiles[i]) div gridW, tile_get_y(tiles[i]) div gridH] = tiles[i];
You can then loop through the visible ones by:
Code:
var l = view_xview[view_current] div gridW;
var r = (view_xview[view_current] + view_wview[view_current] - 1) div gridW;
var t = view_yview[view_current] div gridH;
var d = (view_yview[view_current] + view_hview[view_current] - 1) div gridH;

for(var i = l; i <= r; i++)
    for(var j = t; j <= d; j++)
        if tile_array[i, j] >= 0 {
            // Do something with tile tile_array[i, j]
        }
 
A

anomalous

Guest
Consider using a single draw call.

1. scale your minimap tile size down to the smallest you think works. I'm at 2x2 pixels, but you could do 8x8 for example. Keep in mind you can display them larger in size as needed.
So you would make 8x8 pixels that represent a 64x64 tile for example, if 8x8 is enough for you to work with.
: reduces art, reduces draw size, reduces texture page, may be auto-generated if its small enough/simple and corresponds to your main tiles

2. on create/load map, iterate the drawing of the minimap onto a surface.

3. Draw the part of the surface that corresponds to where the main camera is. (no views)

As others have noted, also no views and stuff background/top/left data into an array/ds_grid as per above.
 
Last edited by a moderator:
G

Greg5000

Guest
Thanks for all the help, definitely provided some food for thought.. will let you know how I get on.
 
G

Greg5000

Guest
One last thing.. does anybody know which is the most efficient way to hide and show tiles? I have experimented with duplicating all tiles onto a seperate layer so I end up with map tiles and main tiles on top of each other. What I then did was hide all map tiles in the main view and hide all main tiles in the map view; however when I ran in debug and profiled this approach it seemed that the most intensive activities were the tile_layer_hide and tile_layer_show functions, I found this surprising as I thought hiding tile layers would have a positive effect... is there a better way to prevent tiles on a specific layer from not drawing?

Also to save hours and hours of time I used the 1D array in conjunction with tile_add to create all the duplicated tiles, this causes a lag right on start up of the game but once all have been created it seems fine, however does anyone know whether using tile_add creates new texture pages for the new tiles (like how adding a sprite does), if so this could explain the drop in frame rate as it would mean more texture swaps if this is the case..
 

jo-thijs

Member
I wouldn't know of a more efficient way to show/hide tiles.
If every tile below a certain depth should be invisible, you could try having an object at that depth clear the view to the background.

In theory it can very simple to hide and show an entire layer of tiles at demand and it could be done in single write operation.
However, it seems GameMaker likes to store for every tile individually whatever it is visible and it shows/hides a layer by setting the visible value of every tile in it.
This takes more time as there are more tiles.

There is no reason why tile_add would create a new texture page.
tile_add adds a tile, not a texture.
 

TheouAegis

Member
Tiles are rendered automatically. It will do you no good to enable one tile layer and then in the same step disable it and enable another one. The tile_layer_hide() and tile_layer_show() functions are the best methods to hide tiles and increase speed but they are meant to only be called rarely. Your whole idea of showing tiles for one view and hiding them for another is faulty.
 
G

Greg5000

Guest
So tile_layer_hide could be called once in the create event and the tiles will remain hidden until tile_layer_show is called again (for example by pressing a keyboard button)? I've been calling these functions in the draw event and they were showing up as big culprits in the profiler, I think I might have found my problem!
 

TheouAegis

Member
Yes. And if you ever really need to read from the tile data ever, the tiles are merely invisible. So if necessary fro whatever reason, you can read the tile data and do whatever you want with it without making the tiles visible again.
 
G

Greg5000

Guest
Thanks for the help, I'm sure this will get my frame rate back up to a good healthy overhead.
 
Top