local Camera = {} Camera.__index = Camera function Camera:new(target, width, height, mouseFollow, screenToWorldScale) local self = setmetatable({}, Camera) self.target = target self.width = width self.height = height self.mouseFollow = mouseFollow or false self.mouseRadius = math.min(self.width, self.height) * 0.35 self.mouseWeight = 0.1 self.screenToWorldScale = screenToWorldScale self.x = target.x + (target.width) / 2 - self.width / 2 self.y = target.y + (target.height) / 2 - self.height / 2 self.vx = 0 self.vy = 0 self.stiffness = 50 self.damping = 2 * math.sqrt(self.stiffness) self.limits = { maxX = nil, minX = nil, maxY = nil, minY = nil, } return self end function Camera:setTarget(target) self.target = target end function Camera:setLimits(minX, maxX, minY, maxY) self.limits.minX = minX self.limits.maxX = maxX self.limits.minY = minY self.limits.maxY = maxY end function Camera:update(dt) if not self.target then return end local cx = self.target.x + (self.target.width or 0) / 2 local cy = self.target.y + (self.target.height or 0) / 2 local targetX = cx - self.width / 2 local targetY = cy - self.height / 2 if self.mouseFollow then local sw = love.graphics.getWidth() local sh = love.graphics.getHeight() local mx, my = love.mouse.getPosition() local offsetX = (mx - sw / 2) * self.screenToWorldScale local offsetY = (my - sh / 2) * self.screenToWorldScale local dist = offsetX * offsetX + offsetY * offsetY if dist > self.mouseRadius * self.mouseRadius then local inv = self.mouseRadius / math.sqrt(dist) offsetX = offsetX * inv offsetY = offsetY * inv end targetX = targetX + offsetX * self.mouseWeight targetY = targetY + offsetY * self.mouseWeight end local accelerationX = (targetX - self.x) * self.stiffness - self.vx * self.damping local accelerationY = (targetY - self.y) * self.stiffness - self.vy * self.damping self.vx = self.vx + accelerationX * dt self.vy = self.vy + accelerationY * dt self.x = self.x + self.vx * dt self.y = self.y + self.vy * dt local lim = self.limits if lim.minX ~= nil then self.x = math.max(lim.minX, self.x) end if lim.maxX ~= nil then self.x = math.min(self.x, lim.maxX - self.width) end if lim.minY ~= nil then self.y = math.max(lim.minY, self.y) end if lim.maxY ~= nil then self.y = math.min(self.y, lim.maxY - self.height) end end function Camera:set(padding) padding = padding or 0 love.graphics.push() local canvasW = love.graphics.getCanvas():getWidth() local canvasH = love.graphics.getCanvas():getHeight() local viewW = self.width + padding * 2 local viewH = self.height + padding * 2 local scaleX = canvasW / viewW local scaleY = canvasH / viewH local scale = math.floor(math.min(scaleX, scaleY)) self._lastScale = scale love.graphics.scale(scale) local snapX = math.floor(self.x) local snapY = math.floor(self.y) love.graphics.translate(-(snapX - padding), -(snapY - padding)) end function Camera:unset() love.graphics.pop() end function Camera:getSubPixelOffset() local dx = self.x - math.floor(self.x) local dy = self.y - math.floor(self.y) return dx, dy end return Camera