YoYoGames / GameMaker-Bugs

Public tracking for GameMaker bugs
23 stars 8 forks source link

In-Game: Add new functions to get surface normals from collisions without having to enable physics #7837

Open jack27121 opened 6 days ago

jack27121 commented 6 days ago

Is your feature request related to a problem?

Say you want to do an involved platformer. For slopes, calculating speed, making different surfaces bouncy. if you collide with some slope, knowing the surface angle would allow you to a lot of control over different things

Describe the solution you'd like

When making sprite-masks or tiles, it would be very handy if you had the option to also define a collision shape. Similar to how you can define points on physics objects (but without needing the physics) and then when colliding with something, and easy way to get information about that collision. Contact point, surface normal. If you collide with a tile-layer, get the specific tile

Describe alternatives you've considered

different approaches have been, having a series of sensors all around the player, and checking the distance to a collision from each, and then comparing them and that way getting a surface normal.

Another way is trying to pre-calculate the surface-normal of different tiles, and making a system to get that back when you collide with a tile. Though you then have to do a bunch of extra steps depending on what side of the tile you collided with, and or if it's rotated/flipped/mirrored.

The same complications come if you want to know the surface normal of rotated objects. There is no easy solution for this problem currently. And requires a LOT of intensive work just to get this info.

Additional context

some steps could be easier if you just used the physics system, since you have to define physics shapes of objects (though not tiles), but more often than not you really don't want to use the physics system because it makes controlling platformer mechanics harder.

tinkerer-red commented 6 days ago

The alternative Stann is talking about here is collision_normal from GMLScripts:

function collision_normal(_x, _y, _r, _obj, _prec=false, _notme=true, _res=0.25) {
    //    Returns a 2D "surface normal" (in degrees) at a given point 
    //    on or near an instance detected within a circular test area.
    //    Makes approximately pi*(rad*rad)/(res*res) collision calls.
    //    If no collision is found, (-1) is returned.
    //
    //            x1,y1             point on the surface, real
    //            rad                 radius of test area (default 4), real
    //            obj                 object or instance (or all), real
    //            res                 resolution of test (default 1), float
    //
    /// GMLscripts.com/license
    var nx = 0;
    var ny = 0;

    if (collision_circle(_x, _y, _r, _obj, _prec, _notme)) {
        var _length = _r*_res*2;
        var _itterator = 1/_res;
        var _i, _j;
        _i=-_r; repeat(_length) {
            _j=-_r; repeat(_length) {
                if (point_distance(0, 0, _i, _j) <= _r) {
                    if (!collision_point(_x+_i, _y+_j, _obj, _prec, _notme)) { nx += _i; ny += _j; }
                    if (!collision_point(_x+_j, _y-_i, _obj, _prec, _notme)) { nx += _j; ny -= _i; }
                    if (!collision_point(_x-_i, _y-_j, _obj, _prec, _notme)) { nx -= _i; ny -= _j; }
                    if (!collision_point(_x-_j, _y+_i, _obj, _prec, _notme)) { nx -= _j; ny += _i; }
                }
            _j+=_itterator; }; //end inner repeat
        _i+=_itterator; }; //end outer repeat

        if (nx == 0 && ny == 0) return (-1);
        return point_direction(0, 0, nx, ny);
    }
    else {
            return (-1);
    }
}

And yes, I'm in agreeance this is quite costly for devs to do, it may be rectified a bit by gmrt optimizations, but over all an inbuilt solution would be ideal here. Though I have no suggestions as to name or syntax for it as it's kind of unique, like move_and_collide is.