Page 1 of 2
Pygame Rect in Love?
Posted: Mon Mar 14, 2016 4:28 am
by 59 Byte
So I'm very used to pygame. I've been using it for a while, and I want to switch over to love for many reasons.
In pygame, there is this handy little object class called a Rect that represents a rectangle. The class has variables (ie. Left, right, top, bottom) that change automatically when the other variables are changed. The class also comes with simple collisions for other Rects and points.
I use this Rect class constantly when working with pygame. My games practically revolve around them. Is there something like this Rect class that can be used in love?
Thank you in advance.
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 4:57 am
by XxHAMADEHxX
I'm also coming from Pygame. I just wanted to add the pygame rect from the documentation to help better understand what OP is trying to do.
http://pygame.org/docstest/ref/rect.html
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 5:59 am
by zorg
From what i've seen, it wouldn't be that hard to write a lib that can do the same things that pygame's rect can.
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 6:09 am
by MadByte
Welcome 59 Byte.
I wonder why so many people coming here from PyGame since it doesn't look much inviting to use.
Is it teached in school classes or something like that?
LÖVE itself doesn't contain any predefined classes for you to use, but you can totally create something like this on your own.
The easiest way would be to use an OOP library like
Middleclass or
Classic.
Here is a small example on how I would start to implement this. (unpack to see the code)
Rect.love
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 6:10 am
by Kingdaro
It's fairly trivial to make your own. Here's a part of the implementation I use:
Code: Select all
Rect = {}
Rect.__index = Rect
function Rect.new(width, height, ...)
local self = setmetatable({}, Rect)
self.x, self.y = 0, 0
self.width = width
self.height = height
if ... then self:setPoint(...) end
return self
end
function Rect:getPoint(nx, ny, ox, oy)
local x = self.x + self.width * nx + (ox or 0)
local y = self.y + self.height * ny + (oy or 0)
return x, y
end
function Rect:setPoint(nx, ny, x, y)
self.x = x - self.width * nx
self.y = y - self.height * ny
end
Along with a function for unpacking the rectangle's position and size for convenience, e.g. when using love.graphics.rectangle. This is the whole of it though.
The :getPoint() function returns a position on a rectangle using normalized coordinates, with an optional offset in pixels. :setPoint() sets the position of the rectangle using normalized coordinates as the center.
For the point coordinates, 0 is the left/top of the box, and 1 is the right/bottom on each axis, where 0.5 is the center of each. If you have a box at 0, 0 with a width and height of 100, 200, box:getPoint(0.5, 0.5) == 50, 100. It's a convenient system that takes a lot of math out of the equation.
The constructor optionally accepts any other arguments and passes them to :setPoint().
Example:
Code: Select all
-- create a rect representing the screen
screen = Rect(love.graphics.getDimensions())
-- create a floor, position its bottom middle to the screen's bottom middle, and offset it by 10 pixels up
floor = Rect(800, 50, 0.5, 1, screen:getPoint(0.5, 1, 0, -10))
-- create a player, position its bottom middle to the floor's top middle
player = Rect(80, 80, 0.5, 1, floor:getPoint(0.5, 0))
You can do it pretty much any other way you want, though. It's possible there's something like this lying around somewhere else as well. Tesselode's
drawboxes might be of interest to you.
Aaaand I've been ninja'd twice. Lovely. Oh well. ¯\_(ツ)_/¯
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 7:16 am
by 59 Byte
Thanks a lot for for the responses!
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 7:48 am
by zorg
Okay so i was bored and i used Kingdaro's example as a starting point:
Code: Select all
Rect = {}
Rect.__index = Rect
local new(width, height, ...)
local self = setmetatable({}, Rect)
-- Dimensions
self.width = width
self.height = height
if ... then
self:setPoint(...)
else
-- Do try to set defaults for the topleft of the rectangle, so getPoint doesn't error.
self.setPoint(0, 0)
end
return self
end
function Rect:copy()
return new(self.width, self.height, 0, 0, self.x, self.y)
end
function Rect:getPoint(nx, ny, ox, oy)
local x = self.x + self.width * nx + (ox or 0)
local y = self.y + self.height * ny + (oy or 0)
return x, y
end
function Rect:setPoint(nx, ny, x, y)
self.x = (x or 0) - self.width * nx
self.y = (y or 0) - self.height * ny
end
-- Do note that these do not create new rectangles since that'd be wasting resources,
-- all of them operate 'in place'.
function Rect:translate(tx, ty) -- move_ip
self.x = self.x + (tx or 0)
self.y = self.y + (tx or 0)
end
function Rect:scale(sx,sy,x,y)
self.width = self.width * sx + x
self.height = self.height * sy + y
end
function Rect:scaleCentered(sx,sy,ox,oy) -- inflate_ip, except gives multipliers as well
local w,h = self.width * sx + x, self.height * sy + y
self.x = self.x + self.width / 2 - w / 2
self.y = self.y - self.height / 2 - h / 2
self.width, self.height = w, h
end
function Rect:clamp(rect) -- clamp_ip
-- Move self so it's inside rect, unless self is bigger than rect, in case, center only (per-axis).
if self.width > rect.width then
self.x = rect.x + rect.width / 2 - self.width / 2
else
math.max(math.min(self.x, rect.width), rect.x)
end
if self.height > rect.height then
self.y = rect.y + rect.height / 2 - self.height / 2
else
math.max(math.min(self.y, rect.height), rect.y)
end
end
function Rect:clip(rect) -- clip_ip (doesn't seem to exist)
-- Test for totally-separatedness
if self.x > rect.x + rect.width or
self.x + self.width < rect.x or
self.y > rect.y + rect.height or
self.y + self.height < rect.y
then
self.x = 0
self.y = 0
self.width = 0
self.height = 0
else
-- Unorthodox method, but should work
local x = {self.x, rect.x, self.x+self.width, rect.x+rect.width}
local y = {self.y, rect.y, self.y+self.height, rect.y+rect.height}
table.sort(x); table.sort(y)
self.x, self.y, self.width, self.height = x[2], y[2], x[3]-x[2], y[3]-y[2]
end
function Rect:union(...) -- union(all)_ip
-- take the min of x,y and the max of width,height of all given rects
local infx, infy, supw, suph = math.huge, math.huge, 0, 0
for i,v in ipairs(...) do
infx = math.min(infx, v.x)
infy = math.min(infy, v.y)
supw = math.max(supw, v.width)
suph = math.max(suph, v.height)
end
self.x = math.min(self.x, infx)
self.y = math.min(self.y, infy)
self.width = math.max(self.width, supw)
self.height = math.max(self.height, suph)
end
function Rect:fit(rect) -- fit_ip (doesn't seem to exist)
-- Make it so our rectangle fits inside another
self.x = math.max(self.x, rect.x)
self.y = math.max(self.y, rect.y)
self.width = math.min(self.width, rect.width)
self.height = math.min(self.height, rect.height)
end
-- Normalize doesn't exist since we don't allow negative width or heights.
-- (In other words, x and y will always be the top-left coords of a rect.)
function Rect:contains(rect) -- contains
-- Test whether self is inside rect.
return self.x >= rect.x and self.y >= rect.y and self.width <= rect.width and self.height <= rect.height
end
function Rect:intersects(rect) -- colliderect
return (self.x >= rect.x and self.x <= rect.width) or (self.y >= rect.y and self.y <= rect.height)
end
-- Everything else can be implemented with the above two functions (in terms of collision checking)
-----------
return new
Fair warning, this was written in one go, all untested, but should mostly work.
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 8:01 am
by T-Bone
Wasn't one of the big features of Pygame's Rect, that if you modify some variable on it, like bottom, the rect itself would change accordingly? That's totally doable in Lua, but a bit messy (you'd need to overwrite the __index with a custom function, I guess)
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 8:44 am
by zorg
T-Bone wrote:Wasn't one of the big features of Pygame's Rect, that if you modify some variable on it, like bottom, the rect itself would change accordingly? That's totally doable in Lua, but a bit messy (you'd need to overwrite the __index with a custom function, I guess)
Alternatively use functions like setBottom, but yes, checking the name of the key one wants to reach and then modifying the "real" members accordingly is doable as well.
Re: Pygame Rect in Love?
Posted: Mon Mar 14, 2016 11:29 am
by T-Bone
I just realized something. If I change "bottom" to a larger value, should that mean that the rectangle moves down, or that it becomes taller? That's not obvious to me. Maybe functions like "moveToBottom" and "expandToBottom" are better for clarity.