Skip to content

Instantly share code, notes, and snippets.

@tinkerer-red
Last active April 27, 2024 04:38
Show Gist options
  • Save tinkerer-red/9020a3f1f5fd81d262f7da743eb58f7d to your computer and use it in GitHub Desktop.
Save tinkerer-red/9020a3f1f5fd81d262f7da743eb58f7d to your computer and use it in GitHub Desktop.
more collision functions
//feather ignore all
#region jsDoc
/// @func collision_circle_array()
/// @desc This function is the same as the collision_circle() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances or tile maps.
/// @param {Real} x1 : The x coordinate of the center of the circle to check.
/// @param {Real} y1 : The y coordinate of the center of the circle to check.
/// @param {Real} rad : The radius (distance in pixels from its center to its edge).
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_circle_array(_x1, _y1, _radius, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static __list = ds_list_create();
collision_circle_list(_x1, _y1, _radius, _obj, _prec, _notme, __list, _ordered);
_arr = __push_list_into_arr(_arr, __list);
ds_list_clear(__list);
return _arr;
}
#region jsDoc
/// @func collision_ellipse_array()
/// @desc This function is the same as the collision_ellipse() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances or tile maps.
/// @param {Real} x1 : The x coordinate of the left side of the ellipse to check.
/// @param {Real} y1 : The y coordinate of the top side of the ellipse to check.
/// @param {Real} x2 : The x coordinate of the right side of the ellipse to check.
/// @param {Real} y2 : The y coordinate of the bottom side of the ellipse to check.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_ellipse_array(_x1, _y1, _x2, _y2, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static __list = ds_list_create();
collision_ellipse_list(_x1, _y1, _x2, _y2, _obj, _prec, _notme, __list, _ordered);
_arr = __push_list_into_arr(_arr, __list);
ds_list_clear(__list);
return _arr;
}
#region jsDoc
/// @func collision_line_array()
/// @desc This function is the same as the collision_line() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances / tile maps.
/// @param {Real} x1 : The x coordinate of the start of the line.
/// @param {Real} y1 : The y coordinate of the start of the line.
/// @param {Real} x2 : The x coordinate of the end of the line.
/// @param {Real} y2 : The y coordinate of the end of the line.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_line_array(_x1, _y1, _x2, _y2, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static __list = ds_list_create();
collision_line_list(_x1, _y1, _x2, _y2, _obj, _prec, _notme, __list, _ordered);
_arr = __push_list_into_arr(_arr, __list);
ds_list_clear(__list);
return _arr;
}
#region jsDoc
/// @func collision_point_array()
/// @desc This function is the same as the collision_circle() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances or tile maps.
/// @param {Real} x : The x coordinate of the point to check.
/// @param {Real} y : The y coordinate of the point to check.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_point_array(_x, _y, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static __list = ds_list_create();
collision_point_list(_x, _y, _obj, _prec, _notme, __list, _ordered);
_arr = __push_list_into_arr(_arr, __list);
ds_list_clear(__list);
return _arr;
}
#region jsDoc
/// @func collision_rectangle_array()
/// @desc This function is the same as the collision_circle() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances or tile maps.
/// @param {Real} x1 : The x coordinate of the left side of the rectangle to check.
/// @param {Real} y1 : The y coordinate of the top side of the rectangle to check.
/// @param {Real} x2 : The x coordinate of the right side of the rectangle to check.
/// @param {Real} y2 : The y coordinate of the bottom side of the rectangle to check.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_rectangle_array(_x1, _y1, _x2, _y2, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static __list = ds_list_create();
collision_rectangle_list(_x1, _y1, _x2, _y2, _obj, _prec, _notme, __list, _ordered);
_arr = __push_list_into_arr(_arr, __list);
ds_list_clear(__list);
return _arr;
}
//Abstract Collisions (making use of circle collides)
#region jsDoc
/// @func collision_cone_array()
/// @desc This function is the same as the collision_circle() function, only instead of just detecting one instance / tile map in collision at a time, it will detect multiple instances or tile maps.
/// @param {Real} x : The x coordinate of the left side of the rectangle to check.
/// @param {Real} y : The y coordinate of the top side of the rectangle to check.
/// @param {Real} direction : The direction of the cone check.
/// @param {Real} length : The length of the cone's check distance.
/// @param {Real} fov : The field of view of the cone, a value between 0 and 360.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_cone_array(_x, _y, _direction, _length, _fov, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static _circle_radius = 1_000_000;
if (_fov <= 0 || _length <= 0 || _fov <= 0) return _arr;
var _obj_arr = collision_circle_array(_x, _y, _length, _obj, _prec, _notme, undefined, _ordered);
if (_fov >= 360) return _arr;
var _half_fov = _fov/2;
if (_fov <= 180) {
//daisy chain checks as "and" check
//left
var _cx = _x + lengthdir_x(_circle_radius, _direction-_half_fov+90);
var _cy = _y + lengthdir_y(_circle_radius, _direction-_half_fov+90);
_obj_arr = collision_circle_array(_cx, _cy, _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
//right
_cx = _x + lengthdir_x(_circle_radius, _direction+_half_fov-90);
_cy = _y + lengthdir_y(_circle_radius, _direction+_half_fov-90);
_obj_arr = collision_circle_array(_cx, _cy, _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
return _obj_arr;
}
else {
//check as "or" checks
//left
var _cx = _x + lengthdir_x(_circle_radius, _direction-_half_fov+90);
var _cy = _y + lengthdir_y(_circle_radius, _direction-_half_fov+90);
var _col_arr = collision_circle_array(_cx, _cy, _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
//right
_cx = _x + lengthdir_x(_circle_radius, _direction+_half_fov-90);
_cy = _y + lengthdir_y(_circle_radius, _direction+_half_fov-90);
_col_arr = collision_circle_array(_cx, _cy, _circle_radius, _obj_arr, _prec, _notme, _col_arr, _ordered);
array_unique_ext(_col_arr);
return _col_arr;
}
}
#region jsDoc
/// @func collision_triangle_array()
/// @desc This function will return all instances inside of a triangle, the point order requires the points to be in clockwise order.
/// @param {Real} x1 : The x coordinate of the first line of the triangle to check.
/// @param {Real} y1 : The y coordinate of the first line of the triangle to check.
/// @param {Real} x2 : The x coordinate of the second line of the triangle to check.
/// @param {Real} y2 : The y coordinate of the second line of the triangle to check.
/// @param {Real} x3 : The x coordinate of the third line of the triangle to check.
/// @param {Real} y3 : The y coordinate of the third line of the triangle to check.
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_triangle_array(_x1, _y1, _x2, _y2, _x3, _y3, _obj, _prec=false, _notme=true, _list=ds_list_create(), _ordered=false) {
static _circle_radius = 1_000_000;
//for each line
//Line AB
var _dist_a = point_distance(_x1, _y1, _x2, _y2);
var _dir_a = point_direction(_x1, _y1, _x2, _y2);
var _xa = _x1 + lengthdir_x(_dist_a/2, _dir_a);
var _ya = _y1 + lengthdir_y(_dist_a/2, _dir_a);
var _cdir_a = _dir_a-90;
//Line BC
var _dist_b = point_distance(_x2, _y2, _x3, _y3);
var _dir_b = point_direction(_x2, _y2, _x3, _y3);
var _xb = _x2 + lengthdir_x(_dist_b/2, _dir_b);
var _yb = _y2 + lengthdir_y(_dist_b/2, _dir_b);
var _cdir_b = _dir_b-90;
//Line CA
var _dist_c = point_distance(_x3, _y3, _x1, _y1);
var _dir_c = point_direction(_x3, _y3, _x1, _y1);
var _xc = _x3 + lengthdir_x(_dist_c/2, _dir_c);
var _yc = _y3 + lengthdir_y(_dist_c/2, _dir_c);
var _cdir_c = _dir_c-90;
_obj_arr = collision_circle_array(_xa+lengthdir_x(_circle_radius, _cdir_a), _ya+lengthdir_y(_circle_radius, _cdir_a), _circle_radius, _obj, _prec, _notme, undefined, _ordered);
_obj_arr = collision_circle_array(_xb+lengthdir_x(_circle_radius, _cdir_b), _yb+lengthdir_y(_circle_radius, _cdir_b), _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
_obj_arr = collision_circle_array(_xc+lengthdir_x(_circle_radius, _cdir_c), _yc+lengthdir_y(_circle_radius, _cdir_c), _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
return _obj_arr;
}
#region jsDoc
/// @func collision_polygon_array()
/// @desc This function is for use with convex polygons, (it can work with some concave polygons but not reliable)
/// @param {Array} point_arr : An array of points in a clockwise direction. Format: [x1, y1, x2, y2, x3, y3]
/// @param {Id.TileMapElement | Asset.GMObject | Id.Instance | Constant.All | Constant.Other | Array} obj : Asset or Object Instance or Tile Map Element ID or Array An object, instance, tile map ID, keywords all/other, or array containing these items
/// @param {Bool} prec : Whether the check is based on precise collisions (true, which is slower) or its bounding box in general (false, faster).
/// @param {Bool} notme : Whether the calling instance, if relevant, should be excluded (true) or not (false).
/// @param {Array} array : The array to use to store the IDs of colliding instances.
/// @param {Bool} ordered : Whether the list should be ordered by distance (true) or not (false).
/// @returns {Array}
#endregion
function collision_polygon_array(_point_arr, _obj, _prec=false, _notme=true, _arr=undefined, _ordered=false) {
static _circle_radius = 1_000_000;
var _obj_arr = _obj;
var _length = array_length(_point_arr);
//for each line
var _i=0; repeat(_length/2-1) {
var _x1 = _point_arr[_i+0];
var _y1 = _point_arr[_i+1];
var _x2 = _point_arr[_i+2];
var _y2 = _point_arr[_i+3];
var _dist = point_distance(_x1, _y1, _x2, _y2);
var _dir = point_direction(_x1, _y1, _x2, _y2);
var _x = _x1 + lengthdir_x(_dist/2, _dir);
var _y = _y1 + lengthdir_y(_dist/2, _dir);
var _col_dir = _dir-90;
//check the collision
_obj_arr = collision_circle_array(_x+lengthdir_x(_circle_radius, _col_dir), _y+lengthdir_y(_circle_radius, _col_dir), _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
_i+=2;}//end repeat loop
//check from last point to first point
_x1 = _point_arr[_length-2];
_y1 = _point_arr[_length-1];
_x2 = _point_arr[0];
_y2 = _point_arr[1];
_dist = point_distance(_x1, _y1, _x2, _y2);
_dir = point_direction(_x1, _y1, _x2, _y2);
_x = _x1 + lengthdir_x(_dist/2, _dir);
_y = _y1 + lengthdir_y(_dist/2, _dir);
_col_dir = _dir-90;
//check the collision
_obj_arr = collision_circle_array(_x+lengthdir_x(_circle_radius, _col_dir), _y+lengthdir_y(_circle_radius, _col_dir), _circle_radius, _obj_arr, _prec, _notme, undefined, _ordered);
return _obj_arr;
}
//Intersect functions
#region jsDoc
/// @func line_intersect()
/// @desc Find the intersect of two lines, This function will return undefined if there was no intersect.
/// @param {Real} l1x1 : Line 1, x1.
/// @param {Real} l1y1 : Line 1, y1.
/// @param {Real} l1x2 : Line 1, x2.
/// @param {Real} l1y2 : Line 1, y2.
/// @param {Real} l2x1 : Line 2, x1.
/// @param {Real} l2y1 : Line 2, y1.
/// @param {Real} l2x2 : Line 2, x2.
/// @param {Real} l2y2 : Line 2, y2.
/// @returns {Struct.Point | undefined}
#endregion
function line_intersect(_l1x1, _l1y1, _l1x2, _l1y2, _l2x1, _l2y1, _l2x2, _l2y2) {
var _den = ((_l1x1 - _l1x2) * (_l2y1 - _l2y2)) - ((_l1y1 - _l1y2) * (_l2x1 - _l2x2));
if (_den == 0) {
// Lines are parallel, no intersection
return undefined;
}
var t = ((_l1x1 - _l2x1) * (_l2y1 - _l2y2) - (_l1y1 - _l2y1) * (_l2x1 - _l2x2)) / _den;
var u = -((_l1x1 - _l1x2) * (_l1y1 - _l2y1) - (_l1y1 - _l1y2) * (_l1x1 - _l2x1)) / _den;
if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
var _xx = _l1x1 + t * (_l1x2 - _l1x1);
var _yy = _l1y1 + t * (_l1y2 - _l1y1);
return { x: _xx, y: _yy };
} else {
// Lines don't intersect within the given segments
return undefined;
}
}
#region jsDoc
/// @func line_circle_intersect()
/// @desc Find the intersect of two lines, This function will return undefined if there was no intersect.
/// @param {Real} lx1 : Line x1.
/// @param {Real} ly1 : Line y1.
/// @param {Real} lx2 : Line x2.
/// @param {Real} ly2 : Line y2.
/// @param {Real} cx : Circle x.
/// @param {Real} cy : Circle y.
/// @param {Real} cr : Circle r.
/// @returns {Array.Struct.Point}
#endregion
function line_circle_intersect(_lx1, _ly1, _lx2, _ly2, _cx, _cy, _cr) {
var intersections = [];
var _dx = _lx2 - _lx1;
var _dy = _ly2 - _ly1;
var a = _dx * _dx + _dy * _dy;
var b = 2 * (_dx * (_lx1 - _cx) + _dy * (_ly1 - _cy));
var c = (_lx1 - _cx) * (_lx1 - _cx) + (_ly1 - _cy) * (_ly1 - _cy) - _cr * _cr;
var discriminant = b * b - 4 * a * c;
if (discriminant >= 0) {
// Two possible intersection points
var t1 = (-b + sqrt(discriminant)) / (2 * a);
var t2 = (-b - sqrt(discriminant)) / (2 * a);
var _xx1 = _lx1 + t1 * _dx;
var _yy1 = _ly1 + t1 * _dy;
var _xx2 = _lx1 + t2 * _dx;
var _yy2 = _ly1 + t2 * _dy;
// Check if the intersection point(s) lie on the line segment
if (t1 >= 0 && t1 <= 1) {
array_push(intersections, { x: _xx1, y: _yy1 });
}
if (t2 >= 0 && t2 <= 1) {
array_push(intersections, { x: _xx2, y: _yy2 });
}
}
return (array_length(intersections)) ? intersections : undefined;
}
//helper function
#region jsDoc
/// @func line_intersect()
/// @desc Pushed elements from a ds_list into an array, typically used for conversion but can be used to push a list into an array.
/// @param {Array | undefined} arr : The array to write to. Leaving this argument undefined will generate a new array.
/// @param {Id.DsList} list : the ds_list to read from.
/// @returns {Array | undefined}
#endregion
function __push_list_into_arr(_arr=undefined, _list) {
gml_pragma("forceinline");
if (_arr == undefined) {
_arr = array_create(ds_list_size(_list), undefined);
var _offset = 0;
}
else {
var _offset = array_length(_arr);
array_resize(_arr, _offset+ds_list_size(_list))
}
var _i=0; repeat(ds_list_size(_list)) {
_arr[_offset+_i] = _list[| _i];
_i+=1;}//end repeat loop
return _arr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment