summaryrefslogtreecommitdiff
path: root/camera.lua
blob: 6439a9f7d221f764032d8ce784d815135fdb3408 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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