GMS 2 Bbox_Left & Negative 'X' Speed

trebor777

Member
Hello.
I'm hunting bugs in my projects, and I struggling with some rounding issues and the bbox_left variable.

Somehow, bbox_left uses a float value as a reference point, despite my x being set as an integer value in my code.

here's a bit of logging I do every frame:


Code:
// Frame
X:313  DX:4  FX:4   // GOING To the right:   current x, dx : desired speed, fx: final speed after collision.
A X:317  // After setting X with FX.

// Next frame
BBL:  313 | 313  X-4       // Still going to the right, everything's fine: my bbox_left is 'synced' with my x-4 value
X:317  DX:4  FX:4
A X:321

BBL:  317 | 317  X-4 // Still going to the right,
X:321  DX:4  FX:4
A X:325

BBL:  321 | 321  X-4 // Start going to the left here
X:325  DX:-4  FX:-4
A X:321

BBL:  316 | 317  X-4 // AND WTF ???  My X is 321, it's an Integer... and bbox_left seems floored to X-5
X:321  DX:-4  FX:-4
A X:317

BBL:  312 | 313  X-4
X:317  DX:-4  FX:-4
A X:313

Anyone with a similar issue?
In the end should I use my x-4 or 'bbox_left' ??


This tiny issue basically f***s up my collision detection... :/ because it allows the bbox to be 1 pixel off.. and that messes up my 'movable' distance calculation.
:/

Code:
var ndx = 0;
if direction_value != 0 {
    dx = direction_value * move_speed + x_remainder * abs(sign(direction_value));
  
    // DO X collision
    was_on_slope = false;
  
    ndx = scr_collisions_x(dx)  
    show_debug_message("X:" + string(x) + "  DX:" + string(dx) +  "  FX:" + string(ndx > 0 ? floor(ndx) : ceil(ndx)));
    x += ndx > 0 ? floor(ndx) : ceil(ndx);
    show_debug_message("A X:" + string(x))
    x_remainder = frac(ndx);
}

Since i'm fairly new to GMS, i'm wondering if i'm missing something...
 

TheouAegis

Member
How you get the values for BBL printed out? Just bbox_left? How you get that | discrepancy?

bbox_left seemed to be rounded off on GMS. If you round x, i would avoid that personally. floor it or if you can convert it to a signed int, do that..
 

NightFrost

Member
Bbox_* values are (always) rounded integer values of instance coordinate values, and if memory serves, they get updated at the exact same moment a coordinate value changes. If you're getting floats out of them... chances are you're doing something wrong.
 

TheouAegis

Member
I don't think he is getting a float value, I think his issue is GM is rounding the bounding box coordinates one way and he is rounding x another way.
 

trebor777

Member
Hello,

as you can see in my code, i'm trying to either floor (if positive) or ceil (if negative) the added value to x. This way I always add the shortest value to x.
I thought using a 'carry' system would help.
So in the end X is always set to an integer value. (or else i wouldn't get that log for x, right?).

And yes i'm just printing bbox_left.

Here's my code for the 'scr_collisions_x' function, where I display the bbox and x values.
My code checks if at the current speed (= movable distance), there is any thing i collide with.
If so, find the shortest distances between all the things I could collide with and move by that.
(following the idea of implementation from http://higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/)

Code:
//// COLLISION X

// where_we going
var _sign = sign(argument[0]);
var delta_x = _sign > 0 ? floor(argument[0]) : ceil(argument[0]);

var delta_y = argument_count > 1 ? argument[1] : 0;

// get side farthest coordinates we're going on bbox.
var side_x = _sign > 0 ? bbox_right : bbox_left;
var test_x = side_x + delta_x;
var test_real_x = x + delta_x

var test_u = bbox_top + delta_y;
var test_d = y-1 + delta_y;

if _sign > 0 {
    var test_l = bbox_left;
    var test_r = test_x;
}   
else {
    var test_l = test_x;
    var test_r = bbox_right;
}

var colltiles = scr_get_tiles_from_rectangle(test_l, test_u, test_r, test_d, tileSize); // log2(tileSize) = 5 => 5 bits to shift for division by tileSize.

var tx = x >> log2(tileSize);
var ty = y >> log2(tileSize);
var slope_tile = tilemap_get(tilemap, tx, ty)       
var slope_index = tile_get_index(slope_tile);   
var on_slope = false;
if scr_is_slope(slope_tile) {
    on_slope = true;
    var t_mirrored = tile_get_mirror(slope_tile);
    var t_flipped = tile_get_flip(slope_tile);
    var slope_x = tx * tileSize + tileXOffsets[slope_index] * tileSize * t_mirrored;
    var slope_y = ty * tileSize + tileYOffsets[slope_index] * tileSize * (1-t_flipped);       
}

if on_slope {
    var new_slope = scr_is_slope_at(x + delta_x, y);
}

was_on_slope = on_slope && !new_slope;


var distances = ds_list_create();
ds_list_add(distances, delta_x)  // ADD OUR current speed as 1st possible value

// Check tiles found in our 'future' collision rectangle.
for(var i=0; i < ds_list_size(colltiles); i++; ) {
           
    var tileInfo = colltiles[|i];
    var tile = tileInfo[0];
    var t_index = tile_get_index(tile);
    var t_mirrored = tile_get_mirror(tile);
    var t_flipped = tile_get_flip(tile);
    var tile_x = tileInfo[1] * tileSize + tileXOffsets[t_index] * tileSize * t_mirrored;
    var tile_y = tileInfo[2] * tileSize + tileYOffsets[t_index] * tileSize * (1-t_flipped);
    var tile_w = tile_x + tileWidths[t_index];
    var tile_h = tile_y + tileHeights[t_index];
       
    //// check: are we still within that tile using its' real rectangle (for half tiles mostly)  
    if (rectangle_in_rectangle(test_l, test_u, test_r, test_d, tile_x, tile_y, tile_w, tile_h) == 0) {          
        continue;   
    }   

    // Blocks
    if (t_index == 1 || t_index == 4 || t_index == 9) {
        if on_slope and tile_y == slope_y and tile_x == (slope_x + tileSize * _sign) {    // Skip if we're on a slope on the correct side                            
            continue;
        }           
        var distance = side_x - (_sign > 0 ? (tile_x-1) : (tile_w)) // get distance between future bbox side and tile side.
        ds_list_add(distances, distance)                                           
        continue;
    }   
   
    // Slopes (only block from one side)
        // Going Up Right
    if ((t_index == 2 || t_index == 5 || t_index == 7)) {       
        if on_slope and tile_y == slope_y and tile_x == (slope_x + tileSize * _sign) {                           
            continue;
        }
        if _sign < 0 and bbox_right >= tile_w {    // Only block if we're on the opposite side               
            ds_list_add(distances, side_x - (tile_w));
            continue;
        }
    }
   
        // Going Up Left
    if ((t_index == 3 || t_index == 6 || t_index == 8) && _sign > 0) {       
        if on_slope and tile_y == slope_y and tile_x == (slope_x + tileSize * _sign) {                           
            continue;
        }
        if _sign > 0 and bbox_left <= tile_x {    // Only block if we're on the opposite side                                   
            ds_list_add(distances, side_x - (tile_x-1));
            continue
        } 
    }   
}   

show_debug_message("BBL:  " + string(bbox_left) + " | " + string(x-4) +  "  X-4")

//Get shortest distance, multiply it by our current direction.
var r = abs(scr_list_min(distances)) * _sign;
return r;
 

trebor777

Member
Gonna have to rethink some stuff... I made a simple project, just to test the sub-pixel movement principle using a carry, and checked the bbl and x value.
Results where always synced either going left or right. So i must be doing something wrong somewhere.
 

trebor777

Member
Gaaaah Finally understood !!
It's due to the image flipping. Basically my collision mask is set at
l: 20 , r 28 (x center at 24).
But when you flip with image_xscale, the bbox_left value is actually the bbox_right 'converted' as left.
So.. basically to cover 8 pixel, my right coordinate should be 27 if I had set it up correctly.

When it gets converted, 28, bbox_right becomes '19' (because 24 to 28 is 5 pixels) rather than 20 and get assigned to bbox_left hence my 'desync' only when going left.

Notice it by not having a symmetrical collision mask. (like 0, 28) and drew it on screen. Because I had set it up symmetrically, i didn't notice that it was flipped with the x_scale. (I thought it was only the graphics).

That should help a lot in solving some collision issues I have.
 

TheouAegis

Member
I kind of blame myself that I didn't even consider that. I'm glad you figured out on your own what a lot of people don't ever figure out. I just assumed you used a full sprite bounding box like I'm used to seeing on this forum.
 
Top