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
120
121
122
|
local map = love.graphics.newImage("map.png")
-- virtual game world
local WORLD_WIDTH = 16 * 30
local WORLD_HEIGHT = 16 * 30
-- camera viewport
local VIEW_WIDTH = 16 * 15
local VIEW_HEIGHT = 16 * 15
-- actual window size
local SCREEN_WIDTH = 16 * 100
local SCREEN_HEIGHT = 9 * 100
local PLAYER_SIZE = 16
local PLAYER_COLOR = {0.2, 0.6, 1, 1}
local player = {
x = WORLD_WIDTH / 2 - PLAYER_SIZE / 2,
y = WORLD_HEIGHT / 2 - PLAYER_SIZE / 2,
speed = 80,
}
local camera = {
x = 0,
y = 0,
follow_speed = 2,
}
-- calcualte offset of camera position to the nearest integer pixel (grid space)
function camera:getShaderOffset()
local sub_x = camera.x - math.floor(camera.x)
local sub_y = camera.y - math.floor(camera.y)
return {sub_x / VIEW_WIDTH, sub_y / VIEW_HEIGHT}
end
local canvas
-- Toggle: when true, shader adds smooth sub-pixel interpolation; when false, integer-only (choppier)
local smooth_shader_enabled = true
function love.load()
canvas = love.graphics.newCanvas(VIEW_WIDTH, VIEW_HEIGHT)
canvas:setFilter("nearest", "nearest")
map:setFilter("nearest", "nearest")
love.window.setMode(SCREEN_WIDTH, SCREEN_HEIGHT)
camera.x = player.x + PLAYER_SIZE / 2 - VIEW_WIDTH / 2
camera.y = player.y + PLAYER_SIZE / 2 - VIEW_HEIGHT / 2
smooth_shader = love.graphics.newShader("smoothCamera.glsl")
end
function love.keypressed(key)
if key == "f1" then
smooth_shader_enabled = not smooth_shader_enabled
end
end
function love.update(dt)
local dx, dy = 0, 0
if love.keyboard.isDown("w") then dy = dy - 1 end
if love.keyboard.isDown("s") then dy = dy + 1 end
if love.keyboard.isDown("a") then dx = dx - 1 end
if love.keyboard.isDown("d") then dx = dx + 1 end
if dx ~= 0 or dy ~= 0 then
local len = math.sqrt(dx * dx + dy * dy)
dx, dy = dx / len, dy / len
player.x = player.x + dx * player.speed * dt
player.y = player.y + dy * player.speed * dt
end
player.x = math.max(0, math.min(WORLD_WIDTH - PLAYER_SIZE, player.x))
player.y = math.max(0, math.min(WORLD_HEIGHT - PLAYER_SIZE, player.y))
local target_x = player.x + PLAYER_SIZE / 2 - VIEW_WIDTH / 2
local target_y = player.y + PLAYER_SIZE / 2 - VIEW_HEIGHT / 2
-- camera follows the target position
camera.x = camera.x + (target_x - camera.x) * camera.follow_speed * dt
camera.y = camera.y + (target_y - camera.y) * camera.follow_speed * dt
-- clamp camera position to the world bounds
camera.x = math.max(0, math.min(WORLD_WIDTH - VIEW_WIDTH, camera.x))
camera.y = math.max(0, math.min(WORLD_HEIGHT - VIEW_HEIGHT, camera.y))
end
function love.draw()
love.graphics.setCanvas(canvas)
love.graphics.clear()
love.graphics.push()
local cam_x = math.floor(camera.x)
local cam_y = math.floor(camera.y)
-- translate the camera to the nearest integer pixel
-- this will allow the shader to work properly by calculating the offset from the nearest integer pixel
love.graphics.translate(-cam_x, -cam_y)
love.graphics.draw(map, 0, 0, 0, 2, 2)
love.graphics.setColor(PLAYER_COLOR)
love.graphics.rectangle("fill", player.x, player.y, PLAYER_SIZE, PLAYER_SIZE)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.pop()
love.graphics.setCanvas()
-- calculate the scale of the viewport to the screen
local scale = math.floor(math.min(SCREEN_WIDTH / VIEW_WIDTH, SCREEN_HEIGHT / VIEW_HEIGHT))
scale = math.max(1, scale)
local draw_w = VIEW_WIDTH * scale
local draw_h = VIEW_HEIGHT * scale
local offset_x = (SCREEN_WIDTH - draw_w) / 2
local offset_y = (SCREEN_HEIGHT - draw_h) / 2
if smooth_shader_enabled then
-- calculate the offset of the camera position to the nearest integer pixel
local offset = camera:getShaderOffset()
-- send the offset to the shader
smooth_shader:send("offset", offset)
-- set the shader
love.graphics.setShader(smooth_shader)
end
-- print on screen if shader is enabled
love.graphics.print("Shader is " .. (smooth_shader_enabled and "enabled" or "disabled"), 10, 10)
love.graphics.draw(canvas, offset_x, offset_y, 0, scale, scale)
love.graphics.setShader()
end
|