Discuss and collaborate on GML scripts

You are not logged in.

- Topics: Active | Unanswered

**GMLdisaster****Member**- Registered: 2009-06-28
- Posts: 2

Firstly, I have used the difference between angles script written by xot and EyeGuy within the rayCircle script (and lineCircle), so please can I upload it xot and EyeGuy? Or send you a PM or something?

...**else** **if** abs(((((dir-ldir)**mod** 360)+540)**mod** 360)-180)>90 **{** *//angle difference script by EyeGuy and xot [[http://www.gmlscripts.com/script/angle_difference]]*

*//is facing wrong way, wont be a collision*

**return** false;**}**

...

Hope you see me!

For 2-d shooters and the like, that have very fast, very small moving projectiles. It's not practical to iterate down the whole line doing collision checks, and the built in functions don't return exactly where the collision took place.

I have 3-4 scripts to upload:

-rayCircle gets the first (nearest to line origin) collision of a line with a circle

-rayRect is same thing for a rectangle

-bullet is a way of iterating through all of one type of object (or group of objects by using a parent) that all have circle or rectangles for collisions.

-the iffy 4th one is lineCircle which is the same as rayCircle with a few extra parameters to make it more flexible, but not as fast probably

They take x and y for the start of the line and circle centre/ top left and bottom right, a direction in degrees of the line and a couple of other tidbits.

It could probably do with some optimization and there are probably better ways to do it using areas of math I don't know... but it might be useful for someone, and I don't write anything useful very often.

Also, I'm new here - so hi all, great site!

Offline

**xot****Administrator**- Registered: 2007-08-18
- Posts: 1,240

Welcome to the forum, glad you like the site. All scripts on the GMLscripts.com website are free to use for any purpose. Scripts posted in the forum belong to their authors unless stated otherwise. Your scripts sound interesting, by all means post them.

*Abusing forum power since 1986.*

Offline

**GMLdisaster****Member**- Registered: 2009-06-28
- Posts: 2

Awesome

In that case I'll put them up. I'm pretty sure I got them all in the right formatting and whatnot.

Rays against rectangles... the simplest way I could think to do it, checks the nearest 2 sides of the rectangle in the right direction. A bit typing heavy but as the typing is done now!

```
/*
** script_rayRect(x0,y0,dir,x1,y1,x2,y2,get)
**
** script returns if a collision is made between a ray and a rectangle
** can also return collision point coordinates to variables resultX and resultY
**
** x0 and y0 are line start point coordinates, dir is line angle between 0 and 360 degrees
** x1,y1,x2,y2 define the rectangle, script only works if x2>x1 and y2>y1
** get determines whether the collision coordinates are returned or not
**
** requires resultX and resultY if returning collision point coordinates
**
** GMLscripts.com
*/
{
//arguments named
var x0,y0,dir,x1,y1,x2,y2,get;
x0=argument0;
y0=argument1;
dir=argument2;
x1=argument3;
y1=argument4;
x2=argument5;
y2=argument6;
get=argument7;
//
//horizontal checks
var ans;
if x0<x1 { //left check
if dir<90 or dir>270 {
ans=floor(y0+tan(degtorad(dir))*(x0-x1)); //corresponding y coordinate
if ans>=y1 and ans<=y2 {//the y coordinate is in range
if get=true {resultX=x1;resultY=ans}
return true; //collision made left
}
}
}
else if x0>x2 { //right check
if dir>90 and dir<270 {
ans=floor(y0+tan(degtorad(dir))*(x0-x2));
if ans>=y1 and ans<=y2 {
if get=true {resultX=x2;resultY=ans}
return true; //collision made right
}
}
}
else if y0>=y1 and y0<=y2 { //inside (relevant line dirs are other way inside)
//horizontal checks
if dir<270 and dir>90 { //left check
ans=floor(y0-tan(degtorad(dir))*(x1-x0));
if ans>=y1 and ans<=y2 {
if get=true {resultX=x1;resultY=ans}
return true; //collision made left (inside)
}
}
else if dir<90 or dir>270 { //right check
ans=floor(y0-tan(degtorad(dir))*(x2-x0));
if ans>=y1 and ans<=y2 {
if get=true {resultX=x2;resultY=ans}
return true; //collision made right (inside)
}
}
//vertical checks
if dir>0 and dir<180 { //top check
ans=floor(x0+tan(degtorad(dir+90))*(y1-y0));
if ans>=x1 and ans<=x2 {
if get=true {resultX=ans;resultY=y1}
return true; //collision made top (inside)
}
}
else if dir>180 and dir<360 { //bottom check
ans=floor(x0+tan(degtorad(dir-90))*(y2-y0));
if ans>=x1 and ans<=x2 {
if get=true {resultX=ans;resultY=y2}
return true; //collision made bottom (inside)
}
}
}
//vertical checks
if y0<y1 { //top check
if dir>180 and dir<360 {
ans=floor(x0-tan(degtorad(dir+90))*(y0-y1));
if ans>=x1 and ans<=x2 {
if get=true {resultX=ans;resultY=y1}
return true; //collision made top
}
}
else {
return false; //no collision
}
}
else if y0>y2 { //bottom check
if dir<180 and dir>0 {
ans=floor(x0-tan(degtorad(dir-90))*(y0-y2));
if ans>=x1 and ans<=x2 {
if get=true {resultX=ans;resultY=y2}
return true; //collision made bottom
}
}
else {
return false; //no collision
}
}
return false;
}
```

Here is the ray vs circle, a teensy bit shorter thankfully! A lot of commenting, otherwise I would forget what I was doing. Not *too* crazy though

```
/*
** script_rayCircle(x0,y0,dir,xc,yc,radius,get)
**
** returns true if collision is made between ray and circle
** can return coordinates to resultX, resultY
**
** x0,y0 start point of line, dir is line direction in degrees
** xc,yc are center of circle, radius is obvious
** if get is true coordinates returned to resultX and resultY
**
** requires variables resultX and resultY if coordinates are to be returned
*/
{
//arguments named
var x0,y0,dir,xc,yc,radius,get;
x0=argument0;
y0=argument1;
dir=argument2;
xc=argument3;
yc=argument4;
radius=argument5;
get=argument6;
//temporary variables
var perpangle,ldir,lang,lhyp,ldis,xi,yi,psq,rsq,swap,offset;
//angle perpendicular to line, sounds cool, radius squared, useful throughout
perpangle=dir+90;
rsq=sqr(radius);
//find intersection point between line and perpendicular, this is in middle of results along line
//uses trig on a triangle circle center C, line start S, line intersect I with a right angle at CIS
//inside angle at ISC
ldir=point_direction(x0,y0,xc,yc);
lang=ldir-dir;
lhyp=point_distance(xc,yc,x0,y0); //hypotenuse length
//if ray is pointing away from circle, or inside circle, need to swap answers round
//if inside circle, then will definately have a collision, if ray might stop here
if sqr(lhyp)<=rsq { //inside
swap=-1;
if get=false { //if only want number of collisions can stop here
return true;
}
}
else if abs(((((dir-ldir)mod 360)+540)mod 360)-180)>90 { //angle difference script by EyeGuy and xot [[http://www.gmlscripts.com/script/angle_difference]]
//is facing wrong way, wont be a collision
return false;
}
else {
swap=1;
}
//continue to find the intersect point
ldis=cos(degtorad(lang))*lhyp; //adjacent length (line start to point)
xi=x0+lengthdir_x(ldis,dir);
yi=y0+lengthdir_y(ldis,dir);
//needed to get number of solutions
psq=sqr(xi-xc)+sqr(yi-yc); //sqr distance of point to circle center
//if point is outside circles radius, can stop here
if psq>rsq {
return false;
}
else if get=false {
return true;
}
//work out number of solutions and return collision point
//answers are the line intersect -/+ offset along the line
//offset is opposite in triangle CSI, where S is solution
if psq=rsq {
//one solution
resultX=xi;
resultY=yi;
return true;
}
else if psq<rsq {
//two solutions (but only return first one)
offset=sqrt(sqr(radius)-psq);
resultX=xi-lengthdir_x(offset,dir)*swap;
resultY=yi-lengthdir_y(offset,dir)*swap;
return true;
}
}
```

And this one basically just iterates through the specified group of objects to find the nearest hit...

```
/*
** script_bullet(x0,y0,dir,length,object,w,h,method)
**
** returns if and where a ray first touches an instance of object object, and which instance this is
** good for fast moving point projectiles against circles and axis aligned rectangles, but beware iterations
** instead of calling this script many times for different object types, give all obstacles the same parent object
**
** x0 and y0 are line start point coordinates, dir is line angle between 0 and 360 degrees
** length is the line length, use -1 for an infinite ray
** object is the object type to be checked
** w and h are width and height for rectangles (objects x,y used as top left)
** for circles w is the radius (objects x,y used as center)
** method decides how to check the shape, 0 for rectangle, 1 for circle or 2 for bounding box
** if method uses bounding box, the object being checked must have a sprite or mask
**
** returned value is if a collision is made
** requires resultX and resultY variables to return collision points coordinates
** requires resultInst to return which instance was hit
** requires script_rayCircle and script_rayRect to perform checks
**
** GMLscripts.com
*/
{
//arguments named
var x0,y0,dir,length,object,x1,y1,x2,y2,method;
x0=argument0;
y0=argument1;
dir=argument2;
length=argument3;
object=argument4;
w=argument5;
h=argument6;
method=argument7;
//temporary variables
var n,dist,collision,i,inst,hit,newDist,poorUnfortunate,X,Y;
n=instance_number(object); //only have to count once
dist=length; //duplicate variable because this changes between iterations and length must not
collision=false; //no collisions yet
for(i=0;i<n;i+=1) {
inst=instance_find(object,i);
//checks if there is a hit for this inst using appropriate check
hit=false;
if method=0
hit=script_rayRect(x0,y0,dir,inst.x,inst.y,inst.x+w,inst.y+h,true);
else if method=1
hit=(script_rayCircle(x0,y0,dir,inst.x,inst.y,w,1,true)>0);
else
hit=script_rayRect(x0,y0,dir,inst.bbox_left,inst.bbox_top,inst.bbox_right,inst.bbox_bottom,true);
//if there is a hit, is it closest?
if hit=true {
newDist=sqr(resultX-argument0)+sqr(resultY-argument1);
if dist<0 or newDist<dist { //keep track of closest hit
dist=newDist;
X=resultX;
Y=resultY;
poorUnfortunate=inst;
}
if collision=false { //there has been a collision now
collision=true;
}
}
}
//return coordiantes of first collision
if collision=true {
resultX=X;
resultY=Y;
resultInst=poorUnfortunate;
}
return collision;
}
```

I'll put up line vs circle too if anyone thinks it'd be more useful than ray circle (ray circle is the same script with some clutter, and a little functionality, taken out )

script_lineCircle(x0,y0,dir,xc,yc,radius,points,isray)

You can specify to use a ray instead of a line, and it also returns no, closest, furthest or all (closest first, then furthest) intersections with the circle being tested.

This post is plenty long as it is for now though, and I'll want to check these are OK first.

edit: If these are OK I will upload them for the main site? Any suggestions first though?

*Last edited by GMLdisaster (2009-07-03 12:49:37)*

Offline