Legacy GM [Unsolved] Drag and set an area on a grid

Flashpants

Member
Hi

I'm having an issue designing a satisfactory system whereby a player can drag and define an area within a top-down style game. Within the game, the player can define what a room is used for in an translucent overlay that appears when the player is within "construction mode". The game is designed on a 64 x 64 grid.

Please could you let me know how to best implement the above as I am certain there must be a straightforward way of doing this that I am overlooking.

Note that I do not need to just have a mouse select system that snaps to grid, I need to have the the grid squares that are selected to actually change within the overlay so that the game is aware of what squares have been designated as which rooms. I believe that each square will need some kind of variable attributed to it to allow this.

The only way that I can think of achieving this is using a DS grid - I have tried to overcome this using a DS grid which has allowed me to designate each grid square with a value and to change the appearance and the value of the said square by dragging over it, however there is a huge amount of slowdown using this system which I seem unable to overcome. This is because when the room tile overlay is visible in construction mode, the tiles of every square in the room (which is quite large) needs to be drawn every step (I cannot figure out a way of stopping this). The code that I have used for this is below.

This is in the code in the draw event of the DS grid controller object:
Code:
if global.roomView = true // = true when in construction mode
    {
    for (var row = 0; row<ds_grid_height(global.dsGrid); row++) {
    for (var col=0; col<ds_grid_width(global.dsGrid); col++) {
        if (global.dsGrid[# col, row] == -1)  {
            continue;
        }
        var subimage = global.dsGrid[# col, row];
        if subimage = 0
            {
            draw_sprite(spr_test, subimage, col*64, row*64);
            }
    }
    }
    }

The below is the code that I have put within the mouse controller object step event to define the relevant area being mouse dragged:
Code:
if (px != -1 && py != -1) and instance_exists(obj_room1Build)
{

    ds_grid_set_region(global.dsGrid, px div 64, py div 64, mouse_x div 64, mouse_y div 64, 1)
}
Note that although I have attempted to solve this problem with a DS grid, I am very much open to solutions that solve this in another way!

Thank you for any help.
 
P

ph101

Guest
A grid is certainly the right approach (it seems). I'm curious how big a grid it is. 3 thoughts:

1. I'm maybe showing inexperience here but I don't see the need for the continue statement. What's the rationale?
2. Why draw sprites at all at each grid cell? Create a tile at each location, as that would be a much faster way. Change the tile when you need to.
3. I guess the slow down is that every step in the draw event, you are attempting to loop through the entire grid. (and draw a sprite but see above). I guess you are also setting the grid region every step with the mouse as well which probably is pretty slow also. You should come up with a system that looks at the grid location of your mouse and then compares it to what it was last step. Then you only change the values in your grid - and concurrently the tile being drawn - if the mouse has moved into another cell. That way you loop once through the grid once every time you move the mouse into a new cell, rather than (likely) 60 times a second every second so you won't get any slow down I expect. Hope it helps!

edit. in fact you wouldnt need to loop thru whole grid if using tiles, just alter the values required and change the corresponding tile I guess
 
Last edited by a moderator:

Flashpants

Member
@ph101 In relation to your points:

1. To be honest, I stole this code from an online tutorial. My understanding is that it is basically saying that if the value for the grid square is -1 (empty), continue to the next grid square.
2. I did look at drawing tiles instead of sprites but this also led to a fair amount of slowdown. My main problem with this was trying to stop a tile being added when a tile was already present. Otherwise the game slowly slowed down after the construction mode was toggled a few times, because thousands of new tiles were being added each time. I would also need to then link the tiling to the DS Grid values as I am using the ds_grid_set_region to define the area that the mouse is dragged over.
3. Thank you for the pointer (lol) on the mouse select. I'm not sure how you would code determining a change in the mouse has been made but I will look this up.

@The-any-Key Thank you for the suggestion, I'll see if surfaces are the answer. I haven't used them before but it sounds promising.

I will let you know how it goes. Any other suggestions are also welcome.
 

Flashpants

Member
The above has been very helpful. I have made some progress by making use of surfaces and have created a new surface controller object that includes the below in the draw event. I can drag out over an area, this changes the DS grid value that in turn changes the relevant sprite on the new surface (which is exactly what I want). However, although the game is not now slow in construction mode (when all of the sprites for each grid square are visible), once the mouse is clicked and held and an area is being selected, the frame rate drops considerably again. Debug mode shows that this is mainly because of draw_sprite, ds_grid_get and ds_grid_width.

To cut down on draw_sprite (which is the main culprit) I really want to be able to just draw the sprites for the section of the DS grid where the value has been changed by the dragging of the mouse. I believe that this basically requires:

1. saving the values of the grid before mouse press;
2. comparing values of the grid after mouse released to those from before the mouse press; and
3. drawing the sprite on the grid sections that have changed values only.

Is anyone aware of how this could best be accomplished, as I am struggling to find how this could be done with the DS grid functions?

The code in the draw event in the surface controller is:
Code:
if (surface_exists(surface)) {
    if global.roomView = true {
        if (mouse_check_button(mb_left)) {
            surface_set_target(surface);
            ds_grid_set_region(global.dsGrid, px div 64, py div 64, mouse_x div 64, mouse_y div 64, 1);
            for (var row = 0; row<ds_grid_height(global.dsGrid); row++) {
        for (var col=0; col<ds_grid_width(global.dsGrid); col++) {
        if (global.dsGrid[# col, row] == -1)  {
            continue;
        }
        var subimage = global.dsGrid[# col, row];
                draw_sprite(spr_test, subimage, col*64, row*64);
        }
        }
            surface_reset_target();
    }
    draw_surface(surface, 0, 0);

}
} else {
    surface = surface_create(6144, 3840);
    surface_set_target(surface);
    for (var row = 0; row<ds_grid_height(global.dsGrid); row++) {
    for (var col=0; col<ds_grid_width(global.dsGrid); col++) {
        if (global.dsGrid[# col, row] == -1)  {
            continue;
        }
        var subimage = global.dsGrid[# col, row];
        draw_sprite(spr_test, subimage, col*64, row*64);

    }
    }
   
    surface_reset_target();
   
}
 
P

ph101

Guest
I see you didn't address the main issue which is that you are looping thru entire grid every step, which is totally unnecessary. This is why your debug shows these functions as causing eating up the cpu. You really need to stop doing that :p

You wonder how to determine if mouse has moved cell. Create 2 variables eg previous_x_cell previous_y_cell. when your mouse moves compare the current mouse x,ycell with the 2 previous x,y vars. If either are different, you are in a new cell - you then execute your grid loop code (once). You also then reset the previous_x, y cell vars to the current position for next time.

edit. Also, if using a surface, you dont need to draw the sprite every step. Just once, then draw the surface as the sprites have been drawn on to it and it does not refresh/erase itself each step (ie for the surface you should never require a grid loop, that is the point). But also as said there is no reason to use a surface here, because its not needed, it is overcomplicating a relatively simple thing. You can use tiles which are probably faster. You say tiles led to slow down but I don't expect it was the tiles causing the slow down - perhaps you were also erroneously looping thru the whole grid everystep and repeatedly making new tiles unnecessarily (becuase you could just check if a tile exists in the spot and change it if necessary). Actually from what I see this situation does not even need a grid because you could just create a tile at each grid position and check what type it is and change it if necessary. However you could store the tile id in the grid. Look up tile_add and tile_layer_find and related functions.
 
Last edited by a moderator:
Top