local Vec3 = require "lib.utils.vec3" local utils = require "lib.utils.utils" local EPSILON = 0.001 --- @class Camera --- @field position Vec3 --- @field velocity Vec3 --- @field speed number --- @field pixelsPerMeter integer --- @field scale number --- @field animationNode AnimationNode? --- @field animationEndPosition Vec3 --- @field animationBeginPosition Vec3 local camera = { position = Vec3 {}, velocity = Vec3 {}, acceleration = 0.2, speed = 5, pixelsPerMeter = 32, } function camera:getDefaultScale() return love.window.getDesktopDimensions() / (self.pixelsPerMeter * 30) -- 30 meters wide regardless of the actual screen size end camera.__index = camera camera.scale = camera:getDefaultScale() --------------------------------------------------- local controlMap = { cameraMoveUp = Vec3({ 0, -1 }), cameraMoveLeft = Vec3({ -1 }), cameraMoveDown = Vec3({ 0, 1 }), cameraMoveRight = Vec3({ 1 }) } function camera:update(dt) if self.animationNode and self.animationNode.state == "running" then self.animationNode:update(dt) -- тик анимации self.position = utils.lerp(self.animationBeginPosition, self.animationEndPosition, self.animationNode:getValue()) return end -------------------- зум на колесо --------------------- local y = Tree.controls.mouseWheelY if camera.scale > camera:getDefaultScale() * 5 and y > 0 then return end; if camera.scale < camera:getDefaultScale() / 5 and y < 0 then return end; camera.scale = camera.scale + (camera.scale * 0.1 * y) -------------------------------------------------------- local ps = Tree.panning if ps.delta:length() > 0 then local worldDelta = ps.delta:scale(1 / (self.pixelsPerMeter * self.scale)):scale(dt):scale(self.speed) self.velocity = self.velocity + worldDelta elseif utils.any(utils.keys(controlMap), function(el) return Tree.controls:isDown(el) end) then local input = Vec3 {} for k, v in pairs(controlMap) do if Tree.controls:isDown(k) then input = input + v end end input = input:normalize() or Vec3 {} self.velocity = self.velocity:add(input:scale(self.acceleration):scale(dt)) if self.velocity:length() > self.speed then self.velocity = self.velocity:normalize() * self.speed end end self.position = self.position + self.velocity self.velocity = self.velocity - self.velocity * 5 * dt -- магическая формула, которая означает "экспоненциально замедлиться до нуля" if self.velocity:length() < EPSILON then self.velocity = Vec3 {} end end function camera:attach() local wc, hc = love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 love.graphics.push() love.graphics.translate(wc, hc) love.graphics.scale(self.pixelsPerMeter * self.scale) love.graphics.translate(-self.position.x, -self.position.y) end --- @param position Vec3 function camera:toWorldPosition(position) local wx, wy = love.window.getMode() position = Vec3 { (position.x - wx / 2) / self.pixelsPerMeter / self.scale + self.position.x, (position.y - wy / 2) / self.pixelsPerMeter / self.scale + self.position.y } return position end function camera:detach() love.graphics.pop() end --- @param position Vec3 --- @param animationNode AnimationNode function camera:animateTo(position, animationNode) if self.animationNode and self.animationNode.state ~= "finished" then self.animationNode:finish() end self.animationNode = animationNode self.animationEndPosition = position self.animationBeginPosition = self.position self.velocity = Vec3 {} end --- @return Camera local function new() return setmetatable({ }, camera) end return { new = new }