[ 1 post ]  Reply to topicPost new topic 
Rectangle To Slope Collision Code, Say goodbye to all your slopey troubles!
Author Message
 [us]
 Post subject: Rectangle To Slope Collision Code
PostPosted: Tue May 31, 2016 2:00 am 
User avatar
C# Programmer
Member
[*]
[*]
[*]
[*]
Image

The example project is for GM studio 1.2 https://www.dropbox.com/sh/vrqorbpbwdh4 ... pAvja?dl=0
However, the code only uses a few math functions like sqrt(), abs(),min(), max(), and dot_product() so it should be use-able for any GM version.

However if you're only interested in the code, here's the script:

Syntax: [ Download ] [ Hide ]
Using gml Syntax Highlighting
var player = argument0;
var triangle = argument1;

/*
This function does not take into account
object flipX or flipY or scaling or non-topLeft sprite origins.
You have to do that yourself.

You would only have to edit the triangle points variables.
The rest of the code remains the same.
*/


//bottom left point
var tPx0 = triangle.x;
var tPy0 = triangle.y + triangle.sprite_height;

//top right point
var tPx1 = triangle.x + triangle.sprite_width;
var tPy1 = triangle.y + triangle.sprite_height + triangle.slopeY;

//bottom right corner
var tPx2 = tPx1;
var tPy2 = tPy0;

var tSlopeX = triangle.slopeX;
var tSlopeY = triangle.slopeY;
var tSlopeMagnitude = sqrt((tSlopeX *tSlopeX) + (tSlopeY * tSlopeY));
//get only the axis of the slope
tSlopeX = tSlopeX / tSlopeMagnitude;
tSlopeY = tSlopeY / tSlopeMagnitude;


var output_intersecting = false;
var output_minIntervalDistance = 100000;
var output_translationAxisX = 0, output_translationAxisY = 0;
var output_pushRectOutX = 0, output_pushRectOutY=0;


var rLeft = player.x;
var rTop = player.y;
var rBottom = rTop + player.sprite_height;
var rRight = rLeft + player.sprite_width;


var minA, minB, maxA, maxB;

var intervalDistance;
var axisX, axisY;


var centerOffsetX = .5 * (rLeft + rRight) - .5* (tPx0 + tPx1);
var centerOffsetY = .5 * (rTop + rBottom) - .5* (tPy0 + tPy1);

//Test for X axis

//=========1. Find if the polygons are currently intersecting ======

axisX = 1;
axisY = 0;

//project the rectangle and triangle onto the X axis
minA = rLeft;
maxA = rRight;

minB = dot_product(axisX,axisY, tPx0,tPy0);
minB = min(minB, dot_product(axisX,axisY, tPx1,tPy1));
minB = min(minB, dot_product(axisX,axisY, tPx2,tPy2));

maxB = dot_product(axisX, axisY, tPx0, tPy0);
maxB = max(maxB, dot_product(axisX, axisY, tPx1, tPy1));
maxB = max(maxB, dot_product(axisX, axisY, tPx2, tPy2));

////Check if the polygon projections are currently intersecting
//if the distance is positive, there is no overlap
if (minA < minB)
    intervalDistance= minB - maxA;
else
    intervalDistance= minA - maxB;

if (intervalDistance> 0)
{
    output_intersecting = false;
    return false;
}
//Check if the current interval distance is the minimum one. If so store
//the interval distance and the current distance.
//This will be used to calculate the minimum translation vector.

intervalDistance = abs(intervalDistance);

if (intervalDistance < output_minIntervalDistance)
{
    output_minIntervalDistance = intervalDistance;
    output_translationAxisX = axisX;
    output_translationAxisY = axisY;

    //this causes the translation axis to be relative to how much to push A out of B
    if (dot_product(centerOffsetX, centerOffsetY, output_translationAxisX, output_translationAxisY) < 0)
    {
        output_translationAxisX = output_translationAxisX * -1;
        output_translationAxisY = output_translationAxisY * -1;
    }
    output_pushRectOutX = output_translationAxisX * output_minIntervalDistance;
    output_pushRectOutY = output_translationAxisY * output_minIntervalDistance;
}


//Test for Y axis

//=========1. Find if the polygons are currently intersecting ======

axisX = 0;
axisY = 1;

//project the rectangle and triangle
minA = rTop;
maxA = rBottom;

minB = dot_product(axisX,axisY, tPx0,tPy0);
minB = min(minB, dot_product(axisX,axisY, tPx1,tPy1));
minB = min(minB, dot_product(axisX,axisY, tPx2,tPy2));

maxB = dot_product(axisX, axisY, tPx0, tPy0);
maxB = max(maxB, dot_product(axisX, axisY, tPx1, tPy1));
maxB = max(maxB, dot_product(axisX, axisY, tPx2, tPy2));

////Check if the polygon projections are currently intersecting
//if the distance is positive so there is no overlap
if (minA < minB)
    intervalDistance= minB - maxA;
else
    intervalDistance= minA - maxB;

if (intervalDistance> 0)
{
    output_intersecting = false;
    return false;
}
//Check if the current interval distance is the minimum one. If so store
//the interval distance and the current distance.
//This will be used to calculate the minimum translation vector.

intervalDistance = abs(intervalDistance);

if (intervalDistance < output_minIntervalDistance)
{
    output_minIntervalDistance = intervalDistance;
    output_translationAxisX = axisX;
    output_translationAxisY = axisY;

    //this causes the translation axis to be relative to how much to push A out of B
    if (dot_product(centerOffsetX, centerOffsetY, output_translationAxisX, output_translationAxisY) < 0)
    {
        output_translationAxisX = output_translationAxisX * -1;
        output_translationAxisY = output_translationAxisY * -1;
    }
    output_pushRectOutX = output_translationAxisX * output_minIntervalDistance;
    output_pushRectOutY = output_translationAxisY * output_minIntervalDistance;
}

//Test for Slope axis

//=========1. Find if the polygons are currently intersecting ======

// we must use the perpendicular axis to the slope
axisX = tSlopeY;
axisY = -tSlopeX;

//project the rectangle and triangle

minA = dot_product(axisX,axisY, rLeft,rTop);
minA = min(minA, dot_product(axisX,axisY, rRight,rTop));
minA = min(minA, dot_product(axisX,axisY, rRight,rBottom));
minA = min(minA, dot_product(axisX,axisY, rLeft,rBottom));

maxA = dot_product(axisX,axisY, rLeft,rTop);
maxA = max(maxA, dot_product(axisX,axisY, rRight,rTop));
maxA = max(maxA, dot_product(axisX,axisY, rRight,rBottom));
maxA = max(maxA, dot_product(axisX,axisY, rLeft,rBottom));

minB = dot_product(axisX,axisY, tPx0,tPy0);
minB = min(minB, dot_product(axisX,axisY, tPx1,tPy1));
minB = min(minB, dot_product(axisX,axisY, tPx2,tPy2));

maxB = dot_product(axisX, axisY, tPx0, tPy0);
maxB = max(maxB, dot_product(axisX, axisY, tPx1, tPy1));
maxB = max(maxB, dot_product(axisX, axisY, tPx2, tPy2));

////Check if the polygon projections are currently intersecting
//if the distance is positive so there is no overlap
if (minA < minB)
    intervalDistance= minB - maxA;
else
    intervalDistance= minA - maxB;

if (intervalDistance> 0)
{
    output_intersecting = false;
    return false;
}
//Check if the current interval distance is the minimum one. If so store
//the interval distance and the current distance.
//This will be used to calculate the minimum translation vector.

intervalDistance = abs(intervalDistance);

if (intervalDistance < output_minIntervalDistance)
{
    output_minIntervalDistance = intervalDistance;
    output_translationAxisX = axisX;
    output_translationAxisY = axisY;

    //this causes the translation axis to be relative to how much to push A out of B
    if (dot_product(centerOffsetX, centerOffsetY, output_translationAxisX, output_translationAxisY) < 0)
    {
        output_translationAxisX = output_translationAxisX * -1;
        output_translationAxisY = output_translationAxisY * -1;
    }
    output_pushRectOutX = output_translationAxisX * output_minIntervalDistance;
    output_pushRectOutY = output_translationAxisY * output_minIntervalDistance;
}

//if we've made it to this point, we know the rectangle and triangle are intersecting
output_intersecting = true;

player.x = player.x + output_pushRectOutX;
player.y = player.y + output_pushRectOutY;

return true;
 


If you're interested in how it works, it's the separating axis theorem, but limited to support only rectangle to triangle overlaps.

For the example project, argument0 refers to the player or rectangle and argument1 refers to the triangle or slope. Triangle objects must have the instance variables "slopeX" and "slopeY" which represents the width and height of the slope. For example, for the 32x32 slopes above, slopeX = 32, slopeY = -32. Both the player and slope have their topLeft as the origin. Here, I just call the script at the end of the step for each triangle object.

The output____ variables give info for whether there is an overlap (output_intersecting), the direction that the rectangle is pushed out of (output_translationAxis), the amount it's pushed (output_minIntervalDistance), and the vector offset it's pushed (output_pushRectOut).

Of course, you can change the script to your liking. It doesn't support flipped sprites, non-top-left sprite origins, or scaling. To support them, you just have to change the points for the triangle and bounds of the rectangle.

If there are any bugs with the code above, let me know and I'll figure it out.

If you do add more support, please share the code for others too. Post it here.

_________________
MFGG TKO (scrapped) - Animations
Image
"It feels that time is better spent on original creations" - Konjak
Focus on the performance, the idea, not the technical bits or details - Milt Kahl
 
Top
Offline 
 
 
« Previous topic | Next topic »
Display posts from previous:  Sort by  
 [ 1 post ]  Reply to topicPost new topic 


Who is online

Users browsing this topic: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group