GUI Collision Tutorial
Introduction
This tutorial will teach you one way to detect whether two GUIs are "touching".
Rectangles
Determining if two GUIs touch is the same as determining whether two rectangles intersect. Let's start off by defining a rectangle:
A basic rectangle class
local Rectangle = {}
function Rectangle.new(...)
local args = {...}
-- Four argument constructor - individual coordinates
if #args == 4 then
return constructFromCoords(...)
-- Two argument constructor - position and size Vector2s
elseif #args == 2 then
return constructFromPositionAndSize(...)
-- No argument constructor - return an uninitialized rectangle
else
return rawConstruct()
end
-- Internally construct a rectangle. top, right,
-- bottom, and left are hard to use by hand
function rawConstruct(top, right, bottom, left)
return setmetatable({
top = top,
right = right,
bottom = bottom,
left = left
}, {__index = Rectangle});
end
-- for construction from coordinates: orders
-- points accordingly
function constructFromCoords(x1, y1, x2, y2)
if x1 > x2 then x1, x2 = x2, x1 end
if y1 > y2 then y1, y2 = y2, y1 end
return rawConstruct(y2, x2, y1, x1)
end
-- for construction from position and size
function constructFromPositionAndSize(pos, size)
return constructFromCoords(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
end
end
Basic Rectangle Methods
We can now add some methods:
function Rectangle:width() return self.right - self.left end
function Rectangle:height() return self.top - self.bottom end
function Rectangle:position() return Vector2.new(self.left , self.bottom ) end
function Rectangle:size() return Vector2.new(self:width(), self:height()) end
function Rectangle:area() return self:width() * self:height() end
function Rectangle:center() return self:position() + self:size()/2 end
That's pretty much all you could want to know about a rectangle, right? Well, except for intersections. We'll get to that.
Using the rectangles
Intersection
But I digress. To determine whether two rectangles intersect, you simply check if they intersect horizontally, and whether they intersect vertically. Horizontally, the rectangles intersect if:
r1.left < r2.right and r2.left < r1.right
And vertically:
r1.bottom < r2.top and r2.bottom < r1.top
Using the above rectangles as an example:
Horizontally: 20 < 60 and 30 < 40 == true Vertically: 10 < 90 and 50 < 30 == false
So the rectangles overlap horizontally, but not vertically. Great! Looking at the picture, you can see there is horizontal overlap.
So now we can add one more method to our rectangle object:
function Rectangle:intersects(other)
return self.left < other.right and other.left < self.right and
self.bottom < other.top and other.bottom < self.top
end
Which lets us write:
print(r1:intersects(r2)) -- false
Getting the intersecting rectangle
We can go one stage further though. Lets not only check whether there's an intersection, but find the size of this intersection. Lets start by defining two rectangles that do intersect:
We want to get the rectangle i, the intersection of r1 and r2. Here is the code to do that:
function Rectangle:intersection(other)
if not self:intersects(other) return nil end
local intersection = Rectangle:new()
intersection.top = math.min(self.top, other.top)
intersection.right = math.min(self.right, other.right)
intersection.bottom = math.max(self.bottom, other.bottom)
intersection.left = math.max(self.left, other.left)
return intersection
end
A more complex case
Using Rectangles with GUIs
Great, so we can do rectangle intersection! But what about GUIs? Well, that's easy: GUIs are rectangles! We just need a way of converting:
function Rectangle.fromGUI(gui)
return Rectangle.new(gui.AbsolutePosition, gui.AbsoluteSize)
end
Now we just do this:
local r1, r2 = Rectangle.fromGUI(gui1), Rectangle.fromGUI(gui2)
if r1:intersects(r2) then
print("The GUIs intersect!")
else
print("The GUIs do not intersect!");
end
This will either print "The GUIs intersect!" or "The GUIs do not intersect!"