• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker Problems with surface_getpixel() and ds_grid_set()

T

Taddio

Guest
Hey,
I'm trying to learn some functions which were on my to-learn list since some time, and I tried to tackle the frowned-upon surface_getpixel().
My idea is to split the map into a ds_grid, whose values will depend on the color of the pixel checked.
In the create event, that works like I want, I can adjust grid size for more or less precision.
But the roadblock came when trying to update the grid values when the pixel is erased.
The idea is to have a destructible terrain which will update the grid when a hole is punched in the surface (think something like Worms), and where I could do semi-random maps based on very small sprites.

Screenshot on top is the level as it's created (this works good as I said)

Screenshot at bottom after a collision. Notice the number should be 0 when it's above anything but a pure red pixel, and 1 when it is red.
The surface updates, the user event runs, but the grid doesn't update as I expect.


My train of thoughts is pretty much commented all the way. Let me know if you have any idea or if you need more code/info.

Code:
//=== obj_world_gen_pixel ===//
/// @description Generate world
col = false;        //Collsion flag triggered by obj_projectile
grid_check_x = 0;    //Will get x grid-position of projectile when colliding
grid_check_y = 0;    //Will get y grid-position of projectile when colliding

#region Surface
_spr_surf = 0;                                            //Will be a sprite of the surface, to save it
spr_ind = 1;                                            //Sprite index of the chunk to load (make random eventually)
application_surface_enable(false);                        //We don't really need it
var _spr_w, _spr_h, _x_scale, _y_scale;
surf_sky = surface_create(room_width, room_height);        //This is the blue sky surface
surf_bg = surface_create(room_width, room_height);        //This is the terrain surface
_spr_w = sprite_get_width(spr_get_pixel);                //Get width of terrain sprite
_spr_h = sprite_get_height(spr_get_pixel);                //Get height of terrain sprite
_x_scale = (room_width/_spr_w);                            //get aspet ration in X
_y_scale = (room_height/_spr_h);                        //Get aspect ratio in Y

//Draw sky background
surface_set_target(surf_sky);
draw_sprite_ext(spr_sky_pixel, 0, 0, 0, room_width, room_height, 0, c_white, 1);
surface_reset_target();

//Draw red sprite for terrain
surface_set_target(surf_bg);
draw_sprite_ext(spr_get_pixel, spr_ind, 0, 0, _x_scale, _y_scale, 0, c_white, 1);
surface_reset_target();
//Copy the surface to a sprite
_spr_surf = sprite_create_from_surface(surf_bg, 0, 0, room_width, room_height, 0, 0, 0, 0);
#endregion

#region Creates world based on red color of bg
max_chunk_w = room_width/TILE_SIZE;                            //Get number of horizontalgrid cell in room
max_chunk_h = room_height/TILE_SIZE;                        //Get number of vertical grid cell in room

terrain_grid = ds_grid_create(max_chunk_w, max_chunk_h);    //This ds_grid holds all terrain values

//Loop variables
var _w, _h, _i, _j, _col, _red, _bin, _xx, _yy, _offset;
draw_set_font(font0);
_offset = (TILE_SIZE/2);

//Loops through grid to find red pixels at middle center of grid
for(_i=0; _i<=max_chunk_w-1; _i++) {                        //Loops through all ds_grid in X
    for(_j=0; _j <=max_chunk_h-1; _j++) {                    //Loops through all ds_grid in Y
        _xx = _i<<4;            //Multiply _i by 16 (TILE SIZE)
        _yy = _j<<4;            //Multiply _j by 16 (TILE SIZE)     
        //Get pixel color of the middle-center of the grid cell
        _col = surface_getpixel(surf_bg, (_xx+_offset), (_yy+_offset));
        //Get the red value
        _red = (color_get_red(_col));
        //Divide so a full red value(255) will equal to 1
        _bin = _red >>7;
        //Set grid position to 1 if pixel is red
        ds_grid_set(terrain_grid, _i, _j, _bin);
        //Draw grid value at grid position
        draw_text(_xx+ _offset, _yy+_offset, string(_bin));
    }
}

#endregion
Code:
//********STEP EVENT******//
if(col){
    event_user(0);
}

Code:
//********USER EVENT 0******//
/// @description updates grid after a collision

var  _i, _j, _col, _red, _xp, _yp;
draw_set_font(font0);    //Set font

//Loops a 2-grid square area around collision point
for(_i=-2; _i<=2; _i++){
    for(_j=-2; _j<=2; _j++){
        _xp = (grid_check_x+_i)<<4;                //Multiply grid_x by 16 (TILE_SIZE) to get pixel position
        _yp = (grid_check_y+_j)<<4;                //Multiply grid_y by 16 (TILE_SIZE) to get pixel position
        _col = surface_getpixel(surf_bg, ((_xp)+(TILE_SIZE/2)), ((_yp)+(TILE_SIZE/2)));        //Get color of surface at pixel position
        _red = (color_get_red(_col));            //Get redness of pixel 
        if(_red == 0){                            //If there is no red, set grid position to 0
            ds_grid_set(terrain_grid, grid_check_x+_i, grid_check_y+_j, 0); 
        }
    }
}
col = false;
Code:
//********DRAW EVENT******//
#region Draw world on a surface

//Draw blue background (sky)
if(!surface_exists(surf_sky)) {
    surf_sky = surface_create(room_width, room_height);
    surface_set_target(surf_sky);
    draw_sprite_ext(spr_sky_pixel, 0, 0, 0, room_width, room_height, 0, c_white, 1);
    surface_reset_target();
    draw_surface(surf_sky, 0, 0);
} else {
    draw_surface(surf_sky, 0, 0);
}

//Draw red BG for terrain
if(!surface_exists(surf_bg)) {
    surf_bg = surface_create(room_width, room_height);
    surface_set_target(surf_bg);
    draw_sprite(_spr_surf, 0, 0, 0);
    surface_reset_target();
    draw_surface(surf_bg, 0, 0);
} else {
    draw_surface(surf_bg, 0, 0);
}

//Draw grid cell value (updates every step for debug purposes, remove on compile)
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
for(var _w=0; _w <= (max_chunk_w-1); _w++) {
    for(var _h=0; _h <= (max_chunk_h-1); _h++) {
        var _cell = ds_grid_get(terrain_grid, _w, _h);
        draw_text((_w<<4)+(TILE_SIZE>>2), (_h<<4)+(TILE_SIZE>>2), _cell);
    }
}

#endregion

Screenshot
 
Last edited by a moderator:

Simon Gust

Member
Code looks ok, a bit messy with the lack of spacing but readable.

The first thing I would check is
grid_check_x and
grid_check_y.

Are they at the right position?
Try updating the whole grid temporarily, to make sure the other code works.
 
T

Taddio

Guest
Code looks ok, a bit messy with the lack of spacing but readable.

The first thing I would check is
grid_check_x and
grid_check_y.

Are they at the right position?
Try updating the whole grid temporarily, to make sure the other code works.
Yeah, sorry, I tend to write compact and overuse colors, regions and comments, it's not that easily readeable outside GMS.
Yep, grid_check_x/y are updating and at correct position. They're passed from the projectile object, as is the collision flag. And I know it's surface_getpixel that's bugging me, because if I simply make it so it sets a disc or region on the grid after a collision, it works well.

I did a couple of line-by-line debugging tests to wrap my head around what it didn't work, and I noticed at the time of pixel-checking, the terrain surface HAS been told what to draw on it (the "hole punching" code in the surface has run), but it's NOT actually drawn on screen yet, I'm guessing because it's Draw event is later to the party than the Step event. Could that be part of the problem? I tried to run Event User 0 one frame later to see if it would change someting, but it didn't, unfortunately.

@Miradur Will check these right now, thanks!
 
Top