Page 1 of 1
[Solved] Checking rotated 2D camera - world boundary
Posted: Mon Feb 09, 2015 7:09 pm
by zorg
In the past weeks, i've been working on a camera system, and it's almost ready in its most basic form; the problem is, i've yet to figure out the following:
In its update function, given the world's and the viewport's dimensions, and the camera's next calculated position, orientation and scale, check whether the viewport is wholly inside of the world, and if not, set it so it stays inside of the world, and does not show anything (the void) outside.
Here's the relevant code that i've been trying to append with the orientation detection:
Code: Select all
-- cam.update(cam,dt) -- uses cam instead of implicit self
-- Limit the camera to the world's boundary, if checks are enabled
-- cam.x and cam.y are the position of the camera, that is, the middle of the viewport; cam.o is its orientation, and cam.s is the scale.
if cam.checkBounds then
-- boundary checking helpers
local topLeft = {}
topLeft.x = cam.x - (viewport.width / 2) / cam.s
topLeft.y = cam.y - (viewport.height / 2) / cam.s
-- check if coords are inside the world boundary
if topLeft.x < 0 then
cam.x = (viewport.width / 2) / cam.s
end
if topLeft.x > world.width - (viewport.width / cam.s) then
cam.x = world.width - (viewport.width / 2) / cam.s
end
if topLeft.y < 0 then
cam.y = (viewport.height / 2) / cam.s
end
if topLeft.y > world.height - (viewport.height / cam.s) then
cam.y = world.height - (viewport.height / 2) / cam.s
end
end
Any help would be appreciated
Re: Checking rotated 2D camera - world boundary
Posted: Mon Feb 09, 2015 8:43 pm
by micha
If I understand correctly you want to know the formula for the position of the screen corners, if the camera is rotated by the angle o.
Here you go:
Code: Select all
local dx = viewport.width/2/cam.s
local dy = viewport.height/2/cam.s
topLeft.x = cam.x - dx * math.cos(cam.o) - dy * math.sin(cam.o)
topLeft.y = cam.y + dx * math.sin(cam.o) - dy * math.cos(cam.o)
Usually I screw up the signs (+/-) before the dx and dy. If the above formula is incorrect, then try replacing some + with - or vice versa.
Re: Checking rotated 2D camera - world boundary
Posted: Tue Feb 10, 2015 2:29 am
by zorg
Thanks micha, that worked! Though i still needed to modify the boundary checks to account for all corners; hopefully the following code is correct; i did test it, and it worked flawlessly for me, so i'm hopeful that it has not any bugs
Code: Select all
if cam.checkBounds then
-- boundary checking helpers
local topLeft = {}
local bottomRight = {}
local hx = viewport.width / 2
local hy = viewport.height / 2
local dx = hx / cam.s
local dy = hy / cam.s
local ax = -dx * math.cos(cam.o) - dy * math.sin(cam.o)
local ay = dx * math.sin(cam.o) - dy * math.cos(cam.o)
topLeft.x = cam.x + ax
topLeft.y = cam.y + ay
bottomRight.x = cam.x - ax
bottomRight.y = cam.y - ay
-- check if the viewport is wholly inside the world boundary, and move it back, if not
if topLeft.x < 0 then cam.x = 0 - ax end
if topLeft.y < 0 then cam.y = 0 - ay end
if bottomRight.x < 0 then cam.x = 0 + ax end
if bottomRight.y < 0 then cam.y = 0 + ay end
if bottomRight.x > world.width + dx + ax then cam.x = world.width + ax end
if bottomRight.y > world.height + dy + ay then cam.y = world.height + ay end
if topLeft.x > world.width + dx - ax then cam.x = world.width - ax end
if topLeft.y > world.height + dy - ay then cam.y = world.height - ay end
end
Edit: nope, there're still a few cases where it shows the outside of the world in some corners, i'll try to fix it. Seems to be the top-right and bottom-left that are problematic; probably will need 8 more if-s.
Re: Checking rotated 2D camera - world boundary
Posted: Tue Feb 10, 2015 6:26 am
by micha
Yes, you need to check for all four corners, if the camera is rotated.
You can reduce the number of if-statements a little bit: First calculate the x coordinate of all four corners. Then find the corner with the largest x-coordinate
Code: Select all
largestX = math.max(topleft.x,topright.x,bottomleft.x,bottomright.x)
And then only compare this largest x to the world boundary:
Code: Select all
if largestX > world.width then
cam.x = cam.x - (largestX - world.width)
end
Repeat for smallest x and largest and smallest y.
The quantity (largestX-world.width) is the amount of overlap.
Re: Checking rotated 2D camera - world boundary
Posted: Tue Feb 10, 2015 8:12 am
by s-ol
micha wrote:Yes, you need to check for all four corners, if the camera is rotated.
You can reduce the number of if-statements a little bit: First calculate the x coordinate of all four corners. Then find the corner with the largest x-coordinate
Code: Select all
largestX = math.max(topleft.x,topright.x,bottomleft.x,bottomright.x)
And then only compare this largest x to the world boundary:
Code: Select all
if largestX > world.width then
cam.x = cam.x - (largestX - world.width)
end
Repeat for smallest x and largest and smallest y.
The quantity (largestX-world.width) is the amount of overlap.
Code: Select all
function minmax( min, max, va, vb )
return math.min(min, va), math.max(max, vb)
end
local maxx, maxy = math.huge, math.huge
local minx, miny = -math.huge, -math.huge
for i,v in ipairs( rects ) do -- or whatever floats your boat
minx, maxx = minmax( minx, maxx, v.x, v.x+v.w )
miny, maxy = minmax( miny, maxy, v.y, v.y+v.h )
---- or ----
minx = math.min( minx, v.x )
miny = math.min ( miny, v.y )
maxx = math.max( maxx, v.x+v.w )
maxy = math.max( maxy, v.y+v.h )
end
Re: Checking rotated 2D camera - world boundary
Posted: Tue Feb 10, 2015 11:10 am
by kikito
Hi there,
I have fought with this problem in my camera library,
gamera.
Here's
the (private) function which calculates the visible area (width and height) based on the angle, zoom factor, and original width and height of the viewport:
Code: Select all
local function getVisibleArea(self, scale)
scale = scale or self.scale
local sin, cos = abs(self.sin), abs(self.cos)
local w,h = self.w / scale, self.h / scale
w,h = cos*w + sin*h, sin*w + cos*h
return min(w,self.ww), min(h, self.wh)
end
You can use that to
adjust the position of the camera so that it does not go beyond the limits of the world.
Code: Select all
local function adjustPosition(self)
local wl,wt,ww,wh = self.wl, self.wt, self.ww, self.wh
local w,h = getVisibleArea(self)
local w2,h2 = w*0.5, h*0.5
local left, right = wl + w2, wl + ww - w2
local top, bottom = wt + h2, wt + wh - h2
self.x, self.y = clamp(self.x, left, right), clamp(self.y, top, bottom)
end
Re: Checking rotated 2D camera - world boundary
Posted: Wed Feb 11, 2015 7:05 pm
by zorg
S0lll0s wrote:(...)
kikito wrote:(...)
Thanks to both of you for helping!
kikito, I tried to somehow integrate your solution into my code, that apparently handled the cam position a bit differently, and i couldn't believe it, it's finally working! One question though, in this case, that you gave two functions, and i modified them to my needs, would i need to paste your license into this, or would an attribution line suffice (like --this part adapted from kikito's gamera <link>-- or something), or how does this work? I'm a bit new to this specific thing.
For posterity, the final code (sans the clamp function):
Code: Select all
if cam.checkBounds then
-- scale the viewport
local dx = viewport.width / cam.s
local dy = viewport.height / cam.s
-- calculate half of that
local hx = dx / 2
local hy = dy / 2
-- this would be your w,h = cos*w + sin*h, sin*w+cos*h code from getVisibleArea(), along with the line below it, and
-- the line local w2,h2 = w*0.5, h*0.5 from adjustPosition()
local ax = math.min((math.abs(math.cos(cam.o))*dx + math.abs(math.sin(cam.o))*dy),world.width ) / 2
local ay = math.min((math.abs(math.sin(cam.o))*dx + math.abs(math.cos(cam.o))*dy),world.height) / 2
-- from adjustPosition()
local left, right = 0 + ax, 0 + world.width - ax
local top, bottom = 0 + ay, 0 + world.height - ay
-- quick and dirty inline, for test purposes only
local clamp = function(n,min,max)
if min>max then min, max = max, min end
return math.min(math.max(n, min), max)
end
cam.x = clamp(cam.x,left,right)
cam.y = clamp(cam.y,top,bottom)
end
Thanks again everyone, for all the help. I'll release this as soon as possible.
Re: Checking rotated 2D camera - world boundary
Posted: Thu Feb 12, 2015 10:51 am
by kikito
Hi, I'm glad you got it working!
Your code is different enough from mine so no you don't need to include my license at all. A reference/link somewhere in your readme (a "credits" section, maybe?) or similar would be nice, but it's totally ok if you don't do it.