SOLVED collision_line algorithm different from draw_line?

Zanhava

Member
I can't pinpoint what I'm doing wrong. The concept is a rectangular object navigating slopes (collision line). Im using draw_line to debug, but it "looks" like the collision line is off by just a few pixels, which is enough to throw off the whole system, but only in a certain quadrant. I'm pretty dumb sometimes so I'm hoping its something simple that I'm breezing over lol

The movement code tests for a possible 1px jump when a standard collision is detected. It works great on slopes going "down", as in starting higher and ending lower (left to right), and mostly works on slopes going up. However there exists a tiny discrepancy somewhere causing unexpected behavior on upward slopes. The rectangle will mount the downward slope from the bottom, but refuses to do so on the upward.

Here is the movement code

GML:
///////////////////input definitions/////////////////////////////////////////////////////

x_change = (keyboard_check(key_move_right)-keyboard_check(key_move_left));
y_change = (keyboard_check(key_move_down)-keyboard_check(key_move_up));

var magnitude = point_distance(0,0,x_change,y_change);
if magnitude > 0
    {
    x_change /= magnitude;
    y_change /= magnitude;
    x_momentum_limit = x_momentum_target_limit/magnitude;
    y_momentum_limit = y_momentum_target_limit/magnitude;
    }

x_momentum += x_change*x_accelerate;
x_momentum =  median(-x_momentum_limit,x_momentum,x_momentum_limit);

y_momentum += y_change*y_accelerate;
y_momentum =  median(-y_momentum_limit,y_momentum,y_momentum_limit);

x_momentum_sign = sign(x_momentum);
x_momentum_abs = abs(x_momentum);
x_momentum_round = floor(x_momentum_abs);
x_momentum_remain += x_momentum_abs-x_momentum_round;

y_momentum_sign = sign(y_momentum);
y_momentum_abs = abs(y_momentum);
y_momentum_round = floor(y_momentum_abs);
y_momentum_remain += y_momentum_abs-y_momentum_round;


if x_momentum_abs < 0.25
    {
    x_momentum = 0;
    };
if x_momentum_remain > 1
    {
    x_momentum_round++;
    x_momentum_remain--;
    //show_debug_message("pop x "+string(x_momentum_remain));
    };
if y_momentum_abs < 0.25
    {
    y_momentum = 0;
    };
if y_momentum_remain > 1
    {
    y_momentum_round++;
    y_momentum_remain--;
    //show_debug_message("pop y "+string(y_momentum_remain));
    };
 
x_count = x_momentum_round;
y_count = y_momentum_round;
var x_climb = true;
var y_climb = true;
var x_move = 0;
var y_move = 0;

/////////////////////physical movement///////////////////////////////////////////////////
if (x_momentum_round+y_momentum_round) > 0
    {
    repeat(max(x_momentum_round,y_momentum_round))
        {
            if x_count > 0
                {
                x+=x_momentum_sign;
                x_count --;
                if line_check()
                    {
                    if x_climb
                        {
                        x_climb = false;
                        y_move = x_momentum_sign*c_data;
                        y+=y_move;
                        if line_check()
                            {
                            y-=y_move;
                            x-=x_momentum_sign;
                            }
                        }
                    else
                        {
                        x-=x_momentum_sign;
                        x_climb = true;
                        }
                    }
                }
            if y_count > 0
                {
                y+=y_momentum_sign;
                y_count --;
                if line_check()
                    {
                        if y_climb
                            {
                            y_climb = false;
                            x_move = y_momentum_sign*c_data;
                            x+=x_move;
                            if line_check()
                                {
                                x-=x_move;
                                y-=y_momentum_sign;
                                }
                            }
                        else
                            {
                            y-=y_momentum_sign;
                            y_climb = true;
                            }
                    }
                }
        }
    }
//////////////////////////////////////////////////////////////////////////////////////////
x_momentum = lerp(x_momentum,0,x_damp);
y_momentum = lerp(y_momentum,0,y_damp);

function line_check()
    {

    for (var i = 0; i < (array_length(line_xy)); ++i)
        {
        var x1 = line_xy[i].x1;
        var x2 = line_xy[i].x2;
        var y1 = line_xy[i].y1;
        var y2 = line_xy[i].y2;
        var check = collision_line(x1,y1,x2,y2,OBJ_1,true,false)
        if check
            {
            var angle = sign(y1-y2)*-1;
            if angle != 0
                {
                c_data = angle;
                };
            show_debug_message(c_data);
            return check;
            }
        }

    }


///////////////////////////debug/////////////////////////////////////////////////////////
if keyboard_check(ord("X"))
    {
        line_xy[2].x2 = get_integer("x value",768);
    }
if keyboard_check(ord("Y"))
    {
        line_xy[2].y2 = get_integer("y value",128);
    }

test_image1.png
^^In the corner, as expected.
test_image2.png
^^collision kicks in 2 pixels and disallows the 1px jump despite graphic...

here is the draw line code
GML:
draw_self();
draw_set_color(c_white);
draw_line(line_xy[0].x1,line_xy[0].y1,line_xy[0].x2,line_xy[0].y2);
draw_line(line_xy[1].x1,line_xy[1].y1,line_xy[1].x2,line_xy[1].y2);
draw_line(line_xy[2].x1,line_xy[2].y1,line_xy[2].x2,line_xy[2].y2);
draw_text(0,0,string(x));
draw_text(0,32,string(y));
here are the line values
GML:
//debug
line_xy[0] = new Line(128,128,128,256);
line_xy[1] = new Line(128,256,512,256);
line_xy[2] = new Line(512,256,512,128);
 
Last edited:

Nidoking

Member
The "climb" calculation looks very wrong to me. You're bumping into the ramp, moving back out of collision, and then if you have any momentum left, you're trying the same movement one pixel higher. If that also doesn't work, you'll loop until you run out of loops and never go anywhere. This tells me you haven't even done algebra properly:

sign(y1-y2)*-1
It's just sign(y2-y1).
 

Zanhava

Member
Correct, there is no early escape and yes, my algebra sucks. But efficiency is not the problem, and I'm trying to correct my algebra.
 

Nidoking

Member
I'm not talking about efficiency. I'm talking about the instance failing to move if the slope is too steep, because it can't move up far enough to clear the slope.
 

Zanhava

Member
Right, which it does do in most cases. A positive y slope works perfect, a negative y slope works only if I fly down on to it from above, in which case it works on the rest of the slope. In the second image, line draw shows a 1px climb from the flat line yet that particular climb fails. If I manually hop the rectangle over this, the rest of the slope works as expected. What is causing my confusion is that it "mostly" works. It's likely I made a mistake similar to the one you pointed out, cause I'm dumb like that (and I am grateful you bothered to read it at all).
The clue to my error is in that, in a line of that angle, there should never be a point where 1px jump is not enough when moving in 1px increments, as demonstrated by the draw line...
 

Nidoking

Member
Oh! Your c_data is only computed based on the direction of the line, when it should be computed entirely based on the direction of movement. You've probably got your instance trying to move down the upward slope. Better yet, get rid of it - you're not colliding with the line if you move downward, so when would you ever need that?
 

Zanhava

Member
I have to admit i'm still lost. Let me think through what i've done. If a collision is detected with the line in the x axis, the x/y_move variables decide which direction to test. If i just use the sign of the momentum, this will work for a positive slope, as heading right will add Y, and heading left will subtract Y. This is where I decided I needed the slope of the angle, the c data. The theory then behind "c_data", was that If player momentum sign worked as expected on positive slopes, I just needed to invert that for negative slopes, as in move right subtract Y, move left add Y. Thus the stupid algebra you found to produce c data. if you are touching a positive slope, c data is 1. multiply that c data by player momentum, and you've done nothing, which is good. Alternatively, when c data reports a negative slope, your momentum value is inverted. Which is good in all cases but the bottom of the slope...problism.png
 

Nidoking

Member
Oh, maybe the problem is that you're doing x and y in the same pass. One moves you, and then the other moves you back.

But the problem I was referring to is that y1 and y2 are arbitrary. I guess if you're sure you're drawing all lines from left to right, that's fine, but if you draw one from right to left, then it's inverted.
 

Zanhava

Member
Yes, what you're saying about the left to right aspect is true, and I figure I'd deal with it when the time comes. Also, discrepancy between draw_line and collision_line is driving me nuts...
dafuq.png
So either I'm doing something very wrong or draw_line and collision_line are using different algorithms.
The first side suggests I can pixel perfect align with the line. Adjust the top x value and the line is now somehow transposed or ignoring the first pixel?
 
Top